#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "database.h"

#include "mqtt_client.h"
#include "mqtt_request_handle.h"

#include "MQTTClient.h"

#include "log_print.h"

#include "common.h"
#include "ll.h"
#include "cJSON.h"



static int token = 0;

extern database_json_t db_config;

/**
  * @brief  获取DataCenter主动发送的token
  * @param  null
  * @retval null
  */
int get_token_str(char *p)
{
    if (NULL == p) return -1;

    token++;
    int_to_char(token, p);

    return 0;
}


/**
  * @brief  字符是否是ascii数字
  * @param  null
  * @retval 0表示是ascii数字，1表示不是ascii数字
  */
int ascii_num_check(char n)
{
    int rtn = 1;

    if ((n >= '0') && (n <= '9')) {
        rtn = 0;
    }

    return rtn;
}


/**
  * @brief  字符是否是ascii 0
  * @param  null
  * @retval 0表示是，1表示不是
  */
int ascii_0_check(char n)
{
    int rtn = 1;

    if (n == '0') {
        rtn = 0;
    }

    return rtn;
}


/**
  * @brief  对时间格式进行校验，格式："2021-08-02T00:00:00.727+0800"
  * @param  null
  * @retval 1表示格式正确，0表示格式错误
  */
int time_valid_check(char *t)
{
    // int year;
    int month;
    int day;
    int hour;
    int min;
    int sec;
    int rtn = 0;

    if (NULL == t) return 0;

    if (('-' != *(t + 4)) || ('-' != *(t + 7)) || ('T' != *(t + 10)) || (':' != *(t + 13)) || (':' != *(t + 16)) || ('.' != *(t + 19))) {
        return 0;
    }

    rtn += ascii_num_check(*(t + 0));
    rtn += ascii_num_check(*(t + 1));
    rtn += ascii_num_check(*(t + 2));
    rtn += ascii_num_check(*(t + 3));
    rtn += ascii_num_check(*(t + 5));
    rtn += ascii_num_check(*(t + 6));
    rtn += ascii_num_check(*(t + 8));
    rtn += ascii_num_check(*(t + 9));
    rtn += ascii_num_check(*(t + 11));
    rtn += ascii_num_check(*(t + 12));
    rtn += ascii_num_check(*(t + 14));
    rtn += ascii_num_check(*(t + 15));
    rtn += ascii_num_check(*(t + 17));
    rtn += ascii_num_check(*(t + 18));
    rtn += ascii_num_check(*(t + 20));
    rtn += ascii_num_check(*(t + 21));
    rtn += ascii_num_check(*(t + 22));

    if (rtn) {
        return 0;
    }

    // year = atoi(t);
    month = atoi(t + 5);
    day = atoi(t + 8);
    hour = atoi(t + 11);
    min = atoi(t + 14);
    sec = atoi(t + 17);

    if ((0 == month) || (month > 12)) return 0;
    if ((0 == day) || (day > 31)) return 0;
    if ((hour > 24)) return 0;
    if ((min > 60)) return 0;
    if ((sec > 60)) return 0;

#if 0
    printf("year = %d \n", year);
    printf("month = %d \n", month);
    printf("day = %d \n", day);
    printf("hour = %d \n", hour);
    printf("min = %d \n", min);
    printf("sec = %d \n", sec);
#endif

    return 1;
}


/**
  * @brief  对时间格式进行校验，格式："2021-08-02T00:00:00.000+0800"，秒钟后面三位小数必须是000
  * @param  null
  * @retval 1表示格式正确，0表示格式错误
  */
int time_valid_check_000(char *t)
{
    // int year;
    int month;
    int day;
    int hour;
    int min;
    int sec;
    int rtn = 0;

    if (NULL == t) return 0;

    if (('-' != *(t + 4)) || ('-' != *(t + 7)) || ('T' != *(t + 10)) || (':' != *(t + 13)) || (':' != *(t + 16)) || ('.' != *(t + 19))) {
        return 0;
    }

    rtn += ascii_num_check(*(t + 0));
    rtn += ascii_num_check(*(t + 1));
    rtn += ascii_num_check(*(t + 2));
    rtn += ascii_num_check(*(t + 3));
    rtn += ascii_num_check(*(t + 5));
    rtn += ascii_num_check(*(t + 6));
    rtn += ascii_num_check(*(t + 8));
    rtn += ascii_num_check(*(t + 9));
    rtn += ascii_num_check(*(t + 11));
    rtn += ascii_num_check(*(t + 12));
    rtn += ascii_num_check(*(t + 14));
    rtn += ascii_num_check(*(t + 15));
    rtn += ascii_num_check(*(t + 17));
    rtn += ascii_num_check(*(t + 18));
    rtn += ascii_0_check(*(t + 20));
    rtn += ascii_0_check(*(t + 21));
    rtn += ascii_0_check(*(t + 22));

    if (rtn) {
        return 0;
    }

    // year = atoi(t);
    month = atoi(t + 5);
    day = atoi(t + 8);
    hour = atoi(t + 11);
    min = atoi(t + 14);
    sec = atoi(t + 17);

    if ((0 == month) || (month > 12)) return 0;
    if ((0 == day) || (day > 31)) return 0;
    if ((hour > 24)) return 0;
    if ((min > 60)) return 0;
    if ((sec > 60)) return 0;

#if 0
    printf("year = %d \n", year);
    printf("month = %d \n", month);
    printf("day = %d \n", day);
    printf("hour = %d \n", hour);
    printf("min = %d \n", min);
    printf("sec = %d \n", sec);
#endif

    return 1;
}


/**
  * @brief  准备回复的数据
  * @param  null
  * @retval null
  */
int mqtt_ack_msg_prepare(cJSON *r_jn, char *token)
{
    char buff[128];

    if ((NULL == r_jn) || (NULL == token)) return -1;

    cJSON_AddItemToObject(r_jn, STR_TOKEN, cJSON_CreateString(token));
    memset(buff, 0x00, sizeof(buff));
    get_timestamp(buff);
    cJSON_AddItemToObject(r_jn, STR_TIMESTAMP, cJSON_CreateString(buff));

    return 0;
}


/**
  * @brief  发送要回复的数据
  * @param  null
  * @retval null
  */
int mqtt_ack_msg_send(cJSON *r_jn, char *response, char *app)
{
    char topic[512];
    char *payload = NULL;
    int payload_len;
    int rtn = 0;

    if (db_config.json_unformatted) {
        payload = cJSON_PrintUnformatted(r_jn);
    } else {
        payload = cJSON_Print(r_jn);
    }
    payload_len = (int)strlen(payload);
    memset(topic, 0x00, sizeof(topic));
    sprintf(topic, response, app);

    if (mqtt_publish_message(topic, payload, payload_len)) {
        rtn = -1;
    }

    if (payload) cJSON_free(payload);

    return rtn;
}


/**
  * @brief  回复OK/FAILURE
  * @param  null
  * @retval null
  */
int mqtt_response_ok_failure(int sta, char *response, char *app, char *token)
{
    char topic[512];
    cJSON *r_jn = NULL;
    char buff[128];
    char *payload = NULL;
    int payload_len;
    int rtn = 0;

    if ((NULL == response) || (NULL == app) || (NULL == token)) {
        rtn = -1;
        goto CLEAN_UP;
    }

    /* 准备回复的数据 */
    r_jn =  cJSON_CreateObject();
    cJSON_AddItemToObject(r_jn, STR_TOKEN, cJSON_CreateString(token));
    memset(buff, 0x00, sizeof(buff));
    get_timestamp(buff);
    cJSON_AddItemToObject(r_jn, STR_TIMESTAMP, cJSON_CreateString(buff));

    if (0 == sta) {
        cJSON_AddItemToObject(r_jn, STR_STATUS, cJSON_CreateString(STR_OK));
    } else {
        cJSON_AddItemToObject(r_jn, STR_STATUS, cJSON_CreateString(STR_FAILURE));
    }

    if (db_config.json_unformatted) {
        payload = cJSON_PrintUnformatted(r_jn);
    } else {
        payload = cJSON_Print(r_jn);
    }
    payload_len = (int)strlen(payload);
    memset(topic, 0x00, sizeof(topic));
    sprintf(topic, response, app);

    if (mqtt_publish_message(topic, payload, payload_len)) {
        //printf("Failed to publish message \n");
        rtn = -2;
        goto CLEAN_UP;
    }

CLEAN_UP:
    if (r_jn) cJSON_Delete(r_jn);
    if (payload) cJSON_free(payload);

    return rtn;
}


/**
  * @brief  设备/APP 注册通知和取消注册通知
  * @param  null
  * @retval null
  */
int topic_update_register_unregister(char *response, cJSON *body)
{
    cJSON *r_jn = NULL;
    char token[64];
    char buff[128];
    char *payload = NULL;
    int payload_len;
    int rtn = 0;

    if ((NULL == response) || (NULL == body)) {
        rtn = -1;
        goto CLEAN_UP;
    }

    /* 准备回复的数据 */
    r_jn =  cJSON_CreateObject();
    memset(token, 0x00, sizeof(token));
    get_token_str(token);
    cJSON_AddItemToObject(r_jn, STR_TOKEN, cJSON_CreateString(token));
    memset(buff, 0x00, sizeof(buff));
    get_timestamp(buff);
    cJSON_AddItemToObject(r_jn, STR_TIMESTAMP, cJSON_CreateString(buff));

    cJSON_AddItemToObject(r_jn, STR_BODY, body);

    if (db_config.json_unformatted) {
        payload = cJSON_PrintUnformatted(r_jn);
    } else {
        payload = cJSON_Print(r_jn);
    }
    payload_len = (int)strlen(payload);

    if (mqtt_publish_message(response, payload, payload_len)) {
        //printf("Failed to publish message \n");
        rtn = -2;
        goto CLEAN_UP;
    }

CLEAN_UP:
    if (r_jn) cJSON_Delete(r_jn);
    if (payload) cJSON_free(payload);

    return rtn;
}


char *err_table[] = {
    /* 0 ~ 9 不要调整这个顺序 */
    "token not found or token is NULL !",
    "timestamp not found or timestamp is NULL !",
    "body not found or body is NULL !",
    "model not found or model is NULL !",
    "[set model] name not found or name is NULL !",
    "[set model] type not found or type is NULL !",
    "[set model] unit not found or unit is NULL !",
    "[set model] deadzone not found or deadzone is NULL !",
    "[set model] ratio not found or ratio is NULL !",
    "[set model] isreport not found or isreport is NULL !",

    /* 10 ~ 19 不要调整这个顺序 */
    "[set model] userdefine not found or userdefine is NULL !",
    "[set model] name oversized !",
    "[set model] type oversized !",
    "[set model] unit oversized !",
    "[set model] deadzone oversized !",
    "[set model] ratio oversized !",
    "[set model] isreport oversized !",
    "[set model] userdefine oversized !",
    "[set dev] model not found or model is NULL !",
    "[set dev] port not found or port is NULL !",

    /* 20 ~ 29 不要调整这个顺序 */
    "[set dev] addr not found or addr is NULL !",
    "[set dev] desc not found or desc is NULL !",
    "[set dev] manuID not found or manuID is NULL !",
    "[set dev] manuName not found or manuName is NULL !",
    "[set dev] ProType not found or ProType is NULL !",
    "[set dev] deviceType not found or deviceType is NULL !",
    "[set dev] isReport not found or isReport is NULL !",
    "[set dev] nodeID not found or nodeID is NULL !",
    "[set dev] productID not found or productID is NULL !",
    "[set dev] model oversized !",

    /* 30 ~ 39 不要调整这个顺序 */
    "[set dev] port oversized !",
    "[set dev] addr oversized !",
    "[set dev] desc oversized !",
    "[set dev] manuID oversized !",
    "[set dev] manuName oversized !",
    "[set dev] ProType oversized !",
    "[set dev] deviceType oversized !",
    "[set dev] isReport oversized !",
    "[set dev] nodeID oversized !",
    "[set dev] productID oversized !",

    /* 40 ~ 49 不要调整这个顺序 */
    "[set dev] regist model before regist device !",
    "[del dev] port not found or port is NULL !",
    "[del dev] addr not found or addr is NULL !",
    "[del dev] desc not found or desc is NULL !",
    "[del dev] dev_table not found !",
    "[del dev] dev not found !",
    "data_row not found or data_row is NULL !",
    "data_row invalid !",
    "regist model and dev before write realtime/frozen data !",
    "frozen_type not found or frozen_type is NULL !",

    /* 50 ~ 59 不要调整这个顺序 */
    "dev not found or dev is NULL !",
    "totalcall not found or totalcall is NULL !",
    "totalcall invalid !",
    "start_time not found or start_time is NULL !",
    "start_time format error !",
    "end_time not found or end_time is NULL !",
    "end_time format error !",
    "time_span not found or time_span is NULL !",
    "frozentype not found or frozentype is NULL !",
    "frozentype invalid !",

    /* 60 ~ 69 不要调整这个顺序 */
    "SourType not found or SourType is NULL !",
    "regist model and dev before write SOE data !",
    "StartSN_item or ReadCnt_item error !",
    "time_type not found or time_type is NULL !",
    "dev or event is NULL !",
    "dev or model error !",
    "[set model] model name > 50 byte !",
    "checkType not found or checkType is NULL !",
    "StartCfgSN not found or StartCfgSN is NULL !",
    "EndCfgSN not found or EndCfgSN is NULL !",

    /* 70 ~ 79 不要调整这个顺序 */
    "time format error, [2021-08-02T00:00:00.727+0800] !",
    "StartCfgSN > EndCfgSN error !",
    "Port not found or Port is NULL !",
    "checkType error, [0, 2] !",
    "checkType error, [0, 1] !",
    "AcqAddr not found or AcqAddr is NULL !",
    "regist model and dev before subscribe SOE event !",
    "subscribe not found or subscribe is NULL !",
    "subscribe error, [0, 1] !",
    "event not found or event is NULL !",

    /* 80 ~ 89 不要调整这个顺序 */
    "HappenSrc not found or HappenSrc is NULL !",
    "HappenCnt not found or HappenCnt is NULL !",
    "TotalTime not found or TotalTime is NULL !",
    "LastHappenTime not found or LastHappenTime is NULL !",
    "LastResumeTime not found or LastResumeTime is NULL !",
    "StartSN not found or StartSN is NULL !",
    "ReadCnt not found or ReadCnt is NULL !",
    "StartSN not a number !",
    "modelname not found or modelname is NULL !",
    "orderNo not found or orderNo is NULL !",

    /* 90 ~ 99 不要调整这个顺序 */
    "upperN not found or upperN is NULL !",
    "license not found or license is NULL !",
    "ESN not found or ESN is NULL !",
    "A model with the same name but different case already exists !"
};


/**
  * @brief  mqtt数据包错误原因返回
  * @param  null
  * @retval null
  */
int mqtt_error_ack(char *str_err, cJSON *token, char *response, char *app)
{
    cJSON *r_jn;
    char temp[64];

    if ((NULL == str_err) || (NULL == response) || (NULL == app)) return -1;

    r_jn =  cJSON_CreateObject();

    if (NULL == token) {
        /* 错误原因是token没有，那就用自己的token */
        memset(temp, 0x00, sizeof(temp));
        get_token_str(temp);
        mqtt_ack_msg_prepare(r_jn, temp);
    } else {
        mqtt_ack_msg_prepare(r_jn, token->valuestring);
    }

    cJSON_AddItemToObject(r_jn, STR_ERROR, cJSON_CreateString(str_err));
	
    mqtt_ack_msg_send(r_jn, response, app);
	
    if (r_jn) cJSON_Delete(r_jn);

    return 0;
}


/**
  * @brief  查询版本信息 处理
  * @param  null
  * @retval null
  * 响应格式：
    {
        "token": "123",
        "timestamp": "2016-06-01T08:21:10.752+0800",
        "version": "ST01.090",
        "compatible": "ST01.010"
    }
  */
int topic_get_versin(char* app, char *tpc, char *msg, char *response)
{
    // char topic[512];
    cJSON *jn = NULL;
    cJSON *token_item = NULL;
    cJSON *time_item = NULL;
    cJSON *r_jn = NULL;
    int rtn = 0;
    char *err_no = NULL;

    log_printf(MSG_VERBOSE, "%s", __FUNCTION__);

#if 0   /* 测试VACUUM */
    log_printf(MSG_WARNING, "db_vacuum start");
    db_vacuum();
    log_printf(MSG_WARNING, "db_vacuum ok");
#endif

    if ((NULL == app) || (NULL == msg) || (NULL == response)) {
        rtn = -1;
        goto CLEAN_UP;
    }

    /* 解析请求的json数据 */
    jn = cJSON_Parse(msg);

    token_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TOKEN);
    if ((0 == token_item) || (0 == token_item->valuestring)) {
        err_no = err_table[0];
        rtn = -2;
        goto CLEAN_UP;
    }
    //printf("token_item = %s \n", token_item->valuestring);

    time_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TIMESTAMP);
    if ((0 == time_item) || (0 == time_item->valuestring)) {
        err_no = err_table[1];
        rtn = -2;
        goto CLEAN_UP;
    }
    //printf("time_item = %s \n", time_item->valuestring);

    /* 准备回复的数据-------------------------------------------------------- */
    r_jn =  cJSON_CreateObject();
    mqtt_ack_msg_prepare(r_jn, token_item->valuestring);

    cJSON_AddItemToObject(r_jn, STR_VERSION, cJSON_CreateString(SOFTWARE_VERSION));
    cJSON_AddItemToObject(r_jn, STR_COMPATIBLE, cJSON_CreateString(COMPATIBLE_VERSION));

    if (mqtt_ack_msg_send(r_jn, response, app)) {
        rtn = -3;
        goto CLEAN_UP;
    }

CLEAN_UP:
    mqtt_error_ack(err_no, token_item, response, app);
    /* 
        有关cjson内存释放：
        1、在cjson中释放内存时，只需要释放父节点就可以，不需要专门管理子节点，子节点会随着父节点的释放一并释放掉
        2、cJSON_Print会单独申请内存，需要调用cJSON_free释放内存
    */
    if (jn) cJSON_Delete(jn);
    if (r_jn) cJSON_Delete(r_jn);

    return rtn;
}


/**
  * @brief  模型名称查询 处理
  * @param  null
  * @retval null
  */
int topic_get_modelschema(char* app, char *tpc, char *msg, char *response)
{
    // char topic[512];
    cJSON *jn = NULL;
    cJSON *token_item = NULL;
    cJSON *time_item = NULL;
    cJSON *r_jn = NULL;
    cJSON *Array = NULL;
    int rtn = 0;
    char *err_no = NULL;

    log_printf(MSG_VERBOSE, "%s", __FUNCTION__);

    if ((NULL == app) || (NULL == msg) || (NULL == response)) {
        rtn = -1;
        goto CLEAN_UP;
    }

    /* 解析请求的json数据 */
    jn = cJSON_Parse(msg);

    token_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TOKEN);
    if ((0 == token_item) || (0 == token_item->valuestring)) {
        err_no = err_table[0];
        rtn = -2;
        goto CLEAN_UP;
    }
    //printf("token_item = %s \n", token_item->valuestring);

    time_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TIMESTAMP);
    if ((0 == time_item) || (0 == time_item->valuestring)) {
        err_no = err_table[1];
        rtn = -2;
        goto CLEAN_UP;
    }
    //printf("time_item = %s \n", time_item->valuestring);

    /* 从数据库获取所有模型名 */
    Array = cJSON_CreateArray();
    get_all_mode_name(Array);

    /* 准备回复的数据-------------------------------------------------------- */
    r_jn =  cJSON_CreateObject();
    mqtt_ack_msg_prepare(r_jn, token_item->valuestring);

    cJSON_AddItemToObject(r_jn, STR_BODY, Array);

    if (mqtt_ack_msg_send(r_jn, response, app)) {
        rtn = -3;
        goto CLEAN_UP;
    }

CLEAN_UP:
    mqtt_error_ack(err_no, token_item, response, app);

    if (jn) cJSON_Delete(jn);
    if (r_jn) cJSON_Delete(r_jn);

    return rtn;
}


/**
  * @brief  删除模型下所有设备的什么表 回调函数
  * @param  null
  * @retval null
  */
static int delete_model_all_dev_what_table_cb(void *para, int argc, char **argv, char **azColName)
{
    int i;
    cJSON *Array;

    if (NULL == para) return 0;

    Array = (cJSON *)para;

    if (NULL != (argv[0])) {
        cJSON_AddItemToArray(Array, cJSON_CreateString(argv[0]));
    }

#if 1   /* 调试 */
    printf("column = %d \n", argc);
    for(i = 0; i < argc; i++){
        printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    }
#endif

    return 0;
}


/**
  * @brief  删除模型下所有设备的什么表
  * @param  null
  * @retval null
  */
int delete_model_all_dev_what_table(char *model, char *what_table)
{
    char str_sql[256];
    cJSON *jn;
    int res = 0;
    int size;
    int i;

    if ((NULL == model) || (NULL == what_table)) return -1;

    jn = cJSON_CreateArray();

    memset(str_sql, 0x00, sizeof(str_sql));
    sprintf(str_sql, SELECT_MODEL_LIKE_WHAT_TABLE, model, what_table);
    //printf("str_sql = %s \n", str_sql);
    db_select_with_cb(str_sql, delete_model_all_dev_what_table_cb, (void *)jn);

    size = cJSON_GetArraySize(jn);
    //printf("size = %d \n", size);

    for (i = 0; i < size; i++) {
        cJSON *item = cJSON_GetArrayItem(jn, i);
        if ((NULL == item) || (NULL == item->valuestring)) {
            continue;
        }
        //printf("item->valuestring = %s \n", item->valuestring);

        res = delete_table(item->valuestring);
        if (0 != res) {
            /* 删除失败 */
            LOG_PRINTF_ERROR( "delete %s table[%s] error", what_table, item->valuestring);
        } else {
            log_printf(MSG_WARNING, "delete %s table[%s]", what_table, item->valuestring);
        }
    }

    return 0;
}


/**
  * @brief  模型删除
  * @param  null
  * @retval null
  */
int delete_model(char *model, char **err_no, char *err_buff)
{
    char table_name[256];
    int res = 0;

    /* 删除模型 */
    memset(table_name, 0x00, sizeof(table_name));
    sprintf(table_name, "%s%s", STR_MODE_, model);
    res = delete_table(table_name);
    if (0 != res) {
        /* 删除模型表出错了 */
        sprintf(err_buff, "func(%s) delete table[%s] error[%d]", __FUNCTION__, table_name, res);
        LOG_PRINTF_ERROR( err_buff);
        *err_no = err_buff;
        return -3;
    } else {
        log_printf(MSG_WARNING, "delete model table[%s] ok", table_name);
    }
    /* 模型删除了，设备列表也删除 */
    memset(table_name, 0x00, sizeof(table_name));
    sprintf(table_name, "%s%s", STR_DEV_, model);
    res = delete_table(table_name);
    if (0 != res) {
        /* 删除设备表出错了 */
        sprintf(err_buff, "func(%s) delete table[%s] error[%d]", __FUNCTION__, table_name, res);
        LOG_PRINTF_ERROR( err_buff);
        *err_no = err_buff;
        return -3;
    } else {
        log_printf(MSG_WARNING, "delete dev table[%s] ok", table_name);
    }

    /* 内存中设备实时数据也需要删除 */
    update_model_list_when_model_unregist(model);

    return 0;
}


/**
  * @brief  指定模型删除 处理
  * @param  null
  * @retval null
  */
int topic_action_deletemodel(char* app, char *tpc, char *msg, char *response)
{
    // char table_name[256];
    cJSON *jn = NULL;
    cJSON *token_item = NULL;
    cJSON *time_item = NULL;
    cJSON *body_item = NULL;
    char err_buff[256];
    int i;
    int size;
    int res = 0;
    int rtn = 0;
    char *err_no = NULL;

    log_printf(MSG_VERBOSE, "%s", __FUNCTION__);

    if ((NULL == app) || (NULL == msg) || (NULL == response)) {
        rtn = -1;
        goto CLEAN_UP;
    }

    /* 解析请求的json数据 */
    jn = cJSON_Parse(msg);

    token_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TOKEN);
    if ((0 == token_item) || (0 == token_item->valuestring)) {
        err_no = err_table[0];
        rtn = -2;
        goto CLEAN_UP;
    }
    //printf("token_item = %s \n", token_item->valuestring);

    time_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TIMESTAMP);
    if ((0 == time_item) || (0 == time_item->valuestring)) {
        err_no = err_table[1];
        rtn = -2;
        goto CLEAN_UP;
    }
    //printf("time_item = %s \n", time_item->valuestring);

    body_item = cJSON_GetObjectItemCaseSensitive(jn, STR_BODY);
    if (0 == body_item) {
        err_no = err_table[2];
        rtn = -2;
        goto CLEAN_UP;
    }
    size = cJSON_GetArraySize(body_item);
    //printf("func(%s) cJSON_GetArraySize = %d \n", __FUNCTION__, size);
    if (0 == size) {
        err_no = err_table[2];
        rtn = -2;
        goto CLEAN_UP;
    }

    for (i = 0; i < size; i++) {
        cJSON *model_item = cJSON_GetArrayItem(body_item, i);
        if ((NULL == model_item) || (NULL == model_item->valuestring)) {
            continue;
        }
        //printf("model = %s \n", model_item->valuestring);

        /* 删除模型 */
        res = delete_model(model_item->valuestring, &err_no, err_buff);
        if (res) {
            rtn = -3;
        }
    }

    mqtt_response_ok_failure(res, response, app, token_item->valuestring);

CLEAN_UP:
    mqtt_error_ack(err_no, token_item, response, app);

    if (jn) cJSON_Delete(jn);

    return rtn;
}


/**
  * @brief  模型内容查询 处理
  * @param  null
  * @retval null
  */
int topic_get_model(char* app, char *tpc, char *msg, char *response)
{
    // char topic[512];
    char table_name[128];
    cJSON *jn = NULL;
    cJSON *token_item = NULL;
    cJSON *time_item = NULL;
    cJSON *body_item = NULL;
    cJSON *r_jn = NULL;
    cJSON *Array = NULL;
    int i;
    int size = 0;
    int rtn = 0;
    char *err_no = NULL;
	int exist = 0;

    log_printf(MSG_VERBOSE, "%s", __FUNCTION__);

    if ((NULL == app) || (NULL == msg) || (NULL == response)) {
        rtn = -1;
        goto CLEAN_UP;
    }

    /* 解析请求的json数据 */
    jn = cJSON_Parse(msg);

    token_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TOKEN);
    if ((0 == token_item) || (0 == token_item->valuestring)) {
        err_no = err_table[0];
        rtn = -2;
        goto CLEAN_UP;
    }
    //printf("token_item = %s \n", token_item->valuestring);

    time_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TIMESTAMP);
    if ((0 == time_item) || (0 == time_item->valuestring)) {
        err_no = err_table[1];
        rtn = -2;
        goto CLEAN_UP;
    }
    //printf("time_item = %s \n", time_item->valuestring);

    body_item = cJSON_GetObjectItemCaseSensitive(jn, STR_BODY);
    if (0 == body_item) {
        err_no = err_table[2];
        rtn = -2;
        goto CLEAN_UP;
    }
    size = cJSON_GetArraySize(body_item);
    //printf("func(%s) cJSON_GetArraySize = %d \n", __FUNCTION__, size);
    if (0 == size) {
        err_no = err_table[2];
        rtn = -2;
        goto CLEAN_UP;
    }

    Array = cJSON_CreateArray();
    for (i = 0; i < size; i++) {    /* 循环实现查询多个模型 */
        cJSON * mode_item = cJSON_GetArrayItem(body_item, i);
        if ((0 == mode_item) || (0 == mode_item->valuestring)) {
            continue;
        }
        //printf("model_name = %s \n", mode_item->valuestring);

        cJSON *object = cJSON_CreateObject();
        cJSON_AddItemToObject(object, STR_MODEL, cJSON_CreateString(mode_item->valuestring));
        cJSON *Array1 = cJSON_CreateArray();

        memset(table_name, 0x00, sizeof(table_name));
		sprintf(table_name, "%s%s", STR_MODE_, mode_item->valuestring);
		
		/*判断是否存在该模型*/
		exist = table_exist_check(table_name);
	    if (1 != exist) {
			memset(table_name, 0x00, sizeof(table_name));
			strcpy(table_name, STR_MODE_);
	    }
		
        /* 从数据库获取模型内容 */
        get_mode_info(table_name, Array1);

        cJSON_AddItemToObject(object, STR_BODY, Array1);
        cJSON_AddItemToArray(Array, object);
    }

    /* 准备回复的数据-------------------------------------------------------- */
    r_jn =  cJSON_CreateObject();
    mqtt_ack_msg_prepare(r_jn, token_item->valuestring);

    cJSON_AddItemToObject(r_jn, STR_BODY, Array);

    if (mqtt_ack_msg_send(r_jn, response, app)) {
        rtn = -3;
        goto CLEAN_UP;
    }

CLEAN_UP:
    mqtt_error_ack(err_no, token_item, response, app);

    if (jn) cJSON_Delete(jn);
    if (r_jn) cJSON_Delete(r_jn);

    return rtn;
}


/**
  * @brief 获取模型设置的json中的属性信息
  * @param  null
  * @retval null
  */
int get_proerty_of_model(cJSON *jn, struct model_property_s *p, char **p_err_no)
{
    if (NULL == jn) return -1;

    cJSON *name_item = cJSON_GetObjectItemCaseSensitive(jn, STR_NAME);
    if ((0 == name_item) || (0 == name_item->valuestring)) {
        *p_err_no = err_table[4];
        return -2;
    }
    //printf("name_item = %s \n", name_item->valuestring);

    cJSON *type_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TYPE);
    if ((0 == type_item) || (0 == type_item->valuestring)) {
        *p_err_no = err_table[5];
        return -2;
    }
    //printf("type_item = %s \n", type_item->valuestring);

    cJSON *unit_item = cJSON_GetObjectItemCaseSensitive(jn, STR_UNIT);
    if ((0 == unit_item) || (0 == unit_item->valuestring)) {
        *p_err_no = err_table[6];
        return -2;
    }
    //printf("unit_item = %s \n", unit_item->valuestring);

    cJSON *deadzone_item = cJSON_GetObjectItemCaseSensitive(jn, STR_DEADZONE);
    if ((0 == deadzone_item) || (0 == deadzone_item->valuestring)) {
        *p_err_no = err_table[7];
        return -2;
    }
    //printf("deadzone_item = %s \n", deadzone_item->valuestring);

    cJSON *ratio_item = cJSON_GetObjectItemCaseSensitive(jn, STR_RATIO);
    if ((0 == ratio_item) || (0 == ratio_item->valuestring)) {
        *p_err_no = err_table[8];
        return -2;
    }
    //printf("ratio_item = %s \n", ratio_item->valuestring);

    cJSON *isreport_item = cJSON_GetObjectItemCaseSensitive(jn, STR_ISREPORT);
    if ((0 == isreport_item) || (0 == isreport_item->valuestring)) {
        *p_err_no = err_table[9];
        return -2;
    }
    //printf("isreport_item = %s \n", isreport_item->valuestring);

    cJSON *userdefine_item = cJSON_GetObjectItemCaseSensitive(jn, STR_USERDEFINE);
    if ((0 == userdefine_item) || (0 == userdefine_item->valuestring)) {
        *p_err_no = err_table[10];
        return -2;
    }
    //printf("userdefine_item = %s \n", userdefine_item->valuestring);

    if (strlen(name_item->valuestring) > sizeof(p->name)) {
        *p_err_no = err_table[11];
        return -3;
    } else {
        memcpy(p->name, name_item->valuestring, strlen(name_item->valuestring));
    }

    if (strlen(type_item->valuestring) > sizeof(p->type)) {
        *p_err_no = err_table[12];
        return -3;
    } else {
        memcpy(p->type, type_item->valuestring, strlen(type_item->valuestring));
    }

    if (strlen(unit_item->valuestring) > sizeof(p->unit)) {
        *p_err_no = err_table[13];
        return -3;
    } else {
        memcpy(p->unit, unit_item->valuestring, strlen(unit_item->valuestring));
    }

    if (strlen(deadzone_item->valuestring) > sizeof(p->deadzone)) {
        *p_err_no = err_table[14];
        return -3;
    } else {
        memcpy(p->deadzone, deadzone_item->valuestring, strlen(deadzone_item->valuestring));
    }

    if (strlen(ratio_item->valuestring) > sizeof(p->ratio)) {
        *p_err_no = err_table[15];
        return -3;
    } else {
        memcpy(p->ratio, ratio_item->valuestring, strlen(ratio_item->valuestring));
    }

    if (strlen(isreport_item->valuestring) > sizeof(p->isreport)) {
        *p_err_no = err_table[16];
        return -3;
    } else {
        memcpy(p->isreport, isreport_item->valuestring, strlen(isreport_item->valuestring));
    }

    if (strlen(userdefine_item->valuestring) > sizeof(p->userdefine)) {
        *p_err_no = err_table[17];
        return -3;
    } else {
        memcpy(p->userdefine, userdefine_item->valuestring, strlen(userdefine_item->valuestring));
    }

    return 0;
}


/**
  * @brief  模型设置 处理
  * @param  null
  * @retval null
  */
int topic_set_model(char* app, char *tpc, char *msg, char *response)
{
    char table_name[128];
    cJSON *jn = NULL;
    cJSON *token_item = NULL;
    cJSON *time_item = NULL;
    cJSON *model_item = NULL;
    cJSON *body_item = NULL;
    int i;
    int size = 0;
    int res = 0;
    int rtn = 0;
    char *err_no = NULL;

    log_printf(MSG_VERBOSE, "%s", __FUNCTION__);

    if ((NULL == app) || (NULL == msg) || (NULL == response)) {
        rtn = -1;
        goto CLEAN_UP;
    }

    /* 解析请求的json数据 */
    jn = cJSON_Parse(msg);

    token_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TOKEN);
    if ((0 == token_item) || (0 == token_item->valuestring)) {
        err_no = err_table[0];
        rtn = -2;
        goto CLEAN_UP;
    }
    //printf("token_item = %s \n", token_item->valuestring);

    time_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TIMESTAMP);
    if ((0 == time_item) || (0 == time_item->valuestring)) {
        err_no = err_table[1];
        rtn = -2;
        goto CLEAN_UP;
    }
    //printf("time_item = %s \n", time_item->valuestring);

    model_item = cJSON_GetObjectItemCaseSensitive(jn, STR_MODEL);
    if ((0 == model_item) || (0 == model_item->valuestring) || (0 == strlen(model_item->valuestring))) {
        err_no = err_table[3];
        rtn = -2;
        goto CLEAN_UP;
    }
    //printf("model_item = %s \n", model_item->valuestring);
    if (MODEL_NAME_LEN_MAX < strlen(model_item->valuestring)) {
        err_no = err_table[66];
        rtn = -2;
        goto CLEAN_UP;
    }

    body_item = cJSON_GetObjectItemCaseSensitive(jn, STR_BODY);
    if (0 == body_item) {
        err_no = err_table[2];
        rtn = -2;
        goto CLEAN_UP;
    }
    size = cJSON_GetArraySize(body_item);
    //printf("func(%s) cJSON_GetArraySize = %d \n", __FUNCTION__, size);
    if (0 == size) {
        err_no = err_table[2];
        rtn = -2;
        goto CLEAN_UP;
    }

    /* 创建模型表 */
    memset(table_name, 0x00, sizeof(table_name));
    sprintf(table_name, "%s%s", STR_MODE_, model_item->valuestring);
    log_printf(MSG_INFO, "creating model table: %s", table_name);
    /* 模型表是否存在：1、不存在就创建模型表，2、已经存在就返回错误 */
    res = creat_table_if_not_exist_model(MODEL_TABLE_STRUCT, table_name);

	if(2 == res)
	{	
		LOG_PRINTF_ERROR( "creating model table: %s error[%d]", table_name, res);
        err_no = err_table[93];
        rtn = -2;
        goto CLEAN_UP;
	}
	
    if (0 != res) {
        LOG_PRINTF_ERROR( "creating model table: %s error[%d]", table_name, res);
        mqtt_response_ok_failure(1, response, app, token_item->valuestring);
        rtn = -3;
        goto CLEAN_UP;
    }

    /* 将属性信息写入模型表 */
    for (i = 0; i < size; i++) {
        cJSON *item = cJSON_GetArrayItem(body_item, i);
        if (0 == item) {
            continue;
        }

        struct model_property_s m_p;
        memset(&m_p, 0x00, sizeof(struct model_property_s));
        res = get_proerty_of_model(item, &m_p, &err_no);
        if (0 != res) {
            mqtt_response_ok_failure(1, response, app, token_item->valuestring);
            rtn = -4;
            goto CLEAN_UP;
        }

        /* 将属性写入模型表 */
        res = save_property_to_model_table(table_name, &m_p);
        if (0 != res) {
            LOG_PRINTF_ERROR( "save_property_to_model_table: %s error[%d]", table_name, res);
            mqtt_response_ok_failure(1, response, app, token_item->valuestring);
            rtn = -5;
            goto CLEAN_UP;
        }
    }

    /* 模型创建成功，在内存创建模型信息 */
    update_model_list_when_model_regist(model_item->valuestring);

    /* 回复成功 */
    mqtt_response_ok_failure(0, response, app, token_item->valuestring);
    //log_printf(MSG_INFO, "func(%s) set model[%s] ok", __FUNCTION__, table_name);

CLEAN_UP:
    mqtt_error_ack(err_no, token_item, response, app);

    if (jn) cJSON_Delete(jn);

    return rtn;
}


/**
  * @brief  设备 GUID 查询 处理
  * @param  null
  * @retval null
  */
int topic_get_guid(char* app, char *tpc, char *msg, char *response)
{
    // char topic[512];
    char table_name[128];
    char guid[128];
    char dev[128];
    cJSON *jn = NULL;
    cJSON *token_item = NULL;
    cJSON *time_item = NULL;
    cJSON *body_item = NULL;
    cJSON *r_jn = NULL;
    cJSON *Array = NULL;
    int i;
    int size = 0;
    int rtn = 0;
    char *err_no = NULL;

    log_printf(MSG_VERBOSE, "%s", __FUNCTION__);

    if ((NULL == app) || (NULL == msg) || (NULL == response)) {
        rtn = -1;
        goto CLEAN_UP;
    }

    /* 解析请求的json数据 */
    jn = cJSON_Parse(msg);

    token_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TOKEN);
    if ((0 == token_item) || (0 == token_item->valuestring)) {
        err_no = err_table[0];
        rtn = -2;
        goto CLEAN_UP;
    }
    //printf("token_item = %s \n", token_item->valuestring);

    time_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TIMESTAMP);
    if ((0 == time_item) || (0 == time_item->valuestring)) {
        err_no = err_table[1];
        rtn = -2;
        goto CLEAN_UP;
    }
    //printf("time_item = %s \n", time_item->valuestring);

    body_item = cJSON_GetObjectItemCaseSensitive(jn, STR_BODY);
    if (0 == body_item) {
        err_no = err_table[2];
        rtn = -2;
        goto CLEAN_UP;
    }
    size = cJSON_GetArraySize(body_item);
    //printf("func(%s) cJSON_GetArraySize = %d \n", __FUNCTION__, size);

    Array = cJSON_CreateArray();
    for (i = 0; i < size; i++) {    /* 循环实现查询多个模型 */
        cJSON * array_item = cJSON_GetArrayItem(body_item, i);
        if (0 == array_item) {
            continue;
        }
        cJSON *mode_item = cJSON_GetObjectItemCaseSensitive(array_item, STR_MODEL);
        if ((0 == mode_item) || (0 == mode_item->valuestring)) {
            continue;
        }
        //printf("mode_item = %s \n", mode_item->valuestring);
        cJSON * port_item = cJSON_GetObjectItemCaseSensitive(array_item, STR_PORT);
        if ((0 == port_item) || (0 == port_item->valuestring)) {
            continue;
        }
        //printf("port_item = %s \n", port_item->valuestring);
        cJSON * addr_item = cJSON_GetObjectItemCaseSensitive(array_item, STR_ADDR);
        if ((0 == addr_item) || (0 == addr_item->valuestring)) {
            continue;
        }
        //printf("addr_item = %s \n", addr_item->valuestring);
        cJSON * desc_item = cJSON_GetObjectItemCaseSensitive(array_item, STR_DESC);
        if ((0 == desc_item) || (0 == desc_item->valuestring)) {
            continue;
        }
        //printf("desc_item = %s \n", desc_item->valuestring);

        /* 设备 GUID */
        memset(table_name, 0x00, sizeof(table_name));
        sprintf(table_name, "%s%s", STR_DEV_, mode_item->valuestring);
        memset(guid, 0x00, sizeof(guid));
        get_dev_guid(table_name, mode_item->valuestring, port_item->valuestring, addr_item->valuestring, desc_item->valuestring, guid);
        //log_printf(MSG_DEBUG, "GIUD = %s", guid);
        if (0 == strlen(guid)) {
            char *model;
            char *port;
            char *addr;
            char *desc;
            if (0 == strlen(mode_item->valuestring)) {
                model = "not set";
            } else {
                model = mode_item->valuestring;
            }
            if (0 == strlen(port_item->valuestring)) {
                port = "not set";
            } else {
                port = port_item->valuestring;
            }
            if (0 == strlen(addr_item->valuestring)) {
                addr = "not set";
            } else {
                addr = addr_item->valuestring;
            }
            if (0 == strlen(desc_item->valuestring)) {
                desc = "not set";
            } else {
                desc = desc_item->valuestring;
            }
            log_printf(MSG_WARNING, "func(%s) model(%s) port(%s) add(%s) desc(%s) no GUID found", __FUNCTION__, model, port, addr, desc);
        }

        cJSON *object = cJSON_CreateObject();
        cJSON_AddItemToObject(object, STR_MODEL, cJSON_CreateString(mode_item->valuestring));
        cJSON_AddItemToObject(object, STR_PORT, cJSON_CreateString(port_item->valuestring));
        cJSON_AddItemToObject(object, STR_ADDR, cJSON_CreateString(addr_item->valuestring));
        cJSON_AddItemToObject(object, STR_DESC, cJSON_CreateString(desc_item->valuestring));
        cJSON_AddItemToObject(object, STR_GUID, cJSON_CreateString(guid));
        memset(dev, 0x00, sizeof(dev));
        sprintf(dev, "%s_%s", mode_item->valuestring, guid);
        cJSON_AddItemToObject(object, STR_DEV, cJSON_CreateString(dev));
        cJSON_AddItemToArray(Array, object);
    }

    /* 准备回复的数据-------------------------------------------------------- */
    r_jn =  cJSON_CreateObject();
    mqtt_ack_msg_prepare(r_jn, token_item->valuestring);

    cJSON_AddItemToObject(r_jn, STR_BODY, Array);

    if (mqtt_ack_msg_send(r_jn, response, app)) {
        rtn = -3;
        goto CLEAN_UP;
    }

CLEAN_UP:
    mqtt_error_ack(err_no, token_item, response, app);

    if (jn) cJSON_Delete(jn);
    if (r_jn) cJSON_Delete(r_jn);

    return rtn;
}


/**
  * @brief 获取设备/APP 注册的json中的设备信息
  * @param  null
  * @retval null
  */
static int get_proerty_of_dev(char *guid, char *app, cJSON *jn, struct dev_property_s *p, char **p_err_no)
{
    if (NULL == jn) return -1;

    cJSON *model_item = cJSON_GetObjectItemCaseSensitive(jn, STR_MODEL);
    if ((0 == model_item) || (0 == model_item->valuestring) || (0 == strlen(model_item->valuestring))) {
        *p_err_no = err_table[18];
        return -2;
    }
    //printf("model_item = %s \n", model_item->valuestring);

    cJSON *port_item = cJSON_GetObjectItemCaseSensitive(jn, STR_PORT);
    if ((0 == port_item) || (0 == port_item->valuestring) || (0 == strlen(port_item->valuestring))) {
        *p_err_no = err_table[19];
        return -2;
    }
    //printf("port_item = %s \n", port_item->valuestring);

    cJSON *addr_item = cJSON_GetObjectItemCaseSensitive(jn, STR_ADDR);
    if ((0 == addr_item) || (0 == addr_item->valuestring) || (0 == strlen(addr_item->valuestring))) {
        *p_err_no = err_table[20];
        return -2;
    }
    //printf("addr_item = %s \n", addr_item->valuestring);

    cJSON *desc_item = cJSON_GetObjectItemCaseSensitive(jn, STR_DESC);
    if ((0 == desc_item) || (0 == desc_item->valuestring)) {
        *p_err_no = err_table[21];
        return -2;
    }
    //printf("desc_item = %s \n", desc_item->valuestring);

    cJSON *manuID_item = cJSON_GetObjectItemCaseSensitive(jn, STR_MANUID);
    if ((0 == manuID_item) || (0 == manuID_item->valuestring) || (0 == strlen(manuID_item->valuestring))) {
        *p_err_no = err_table[22];
        return -2;
    }
    //printf("manuID_item = %s \n", manuID_item->valuestring);

    cJSON *manuName_item = cJSON_GetObjectItemCaseSensitive(jn, STR_MANUNAME);
    if ((0 == manuName_item) || (0 == manuName_item->valuestring)) {
        *p_err_no = err_table[23];
        return -2;
    }
    //printf("manuName_item = %s \n", manuName_item->valuestring);

    cJSON *ProType_item = cJSON_GetObjectItemCaseSensitive(jn, STR_PROTYPE);
    if ((0 == ProType_item) || (0 == ProType_item->valuestring)) {
        *p_err_no = err_table[24];
        return -2;
    }
    //printf("ProType_item = %s \n", ProType_item->valuestring);

    cJSON *deviceType_item = cJSON_GetObjectItemCaseSensitive(jn, STR_DEVICETYPE);
    if ((0 == deviceType_item) || (0 == deviceType_item->valuestring)) {
        *p_err_no = err_table[25];
        return -2;
    }
    //printf("deviceType_item = %s \n", deviceType_item->valuestring);

    cJSON *isReport_item = cJSON_GetObjectItemCaseSensitive(jn, STR_ISREPORT);
    if ((0 == isReport_item) || (0 == isReport_item->valuestring)) {
        *p_err_no = err_table[26];
        return -2;
    }
    //printf("isReport_item = %s \n", isReport_item->valuestring);

    cJSON *nodeID_item = cJSON_GetObjectItemCaseSensitive(jn, STR_NODEID);
    if ((0 == nodeID_item) || (0 == nodeID_item->valuestring)) {
        *p_err_no = err_table[27];
        return -2;
    }
    //printf("nodeID_item = %s \n", nodeID_item->valuestring);

    cJSON *productID_item = cJSON_GetObjectItemCaseSensitive(jn, STR_PRODUCTID);
    if ((0 == productID_item) || (0 == productID_item->valuestring)) {
        *p_err_no = err_table[28];
        return -2;
    }
    //printf("productID_item = %s \n", productID_item->valuestring);

    if (strlen(model_item->valuestring) > sizeof(p->model)) {
        *p_err_no = err_table[29];
        return -3;
    } else {
        memcpy(p->model, model_item->valuestring, strlen(model_item->valuestring));
    }

    if (strlen(port_item->valuestring) > sizeof(p->port)) {
        *p_err_no = err_table[30];
        return -3;
    } else {
        memcpy(p->port, port_item->valuestring, strlen(port_item->valuestring));
    }

    if (strlen(addr_item->valuestring) > sizeof(p->addr)) {
        *p_err_no = err_table[31];
        return -3;
    } else {
        memcpy(p->addr, addr_item->valuestring, strlen(addr_item->valuestring));
    }

    if (strlen(desc_item->valuestring) > sizeof(p->desc)) {
        *p_err_no = err_table[32];
        return -3;
    } else {
        memcpy(p->desc, desc_item->valuestring, strlen(desc_item->valuestring));
    }

    if (strlen(manuID_item->valuestring) > sizeof(p->manuID)) {
        *p_err_no = err_table[33];
        return -3;
    } else {
        memcpy(p->manuID, manuID_item->valuestring, strlen(manuID_item->valuestring));
    }

    if (strlen(manuName_item->valuestring) > sizeof(p->manuName)) {
        *p_err_no = err_table[34];
        return -3;
    } else {
        memcpy(p->manuName, manuName_item->valuestring, strlen(manuName_item->valuestring));
    }

    if (strlen(ProType_item->valuestring) > sizeof(p->ProType)) {
        *p_err_no = err_table[35];
        return -3;
    } else {
        memcpy(p->ProType, ProType_item->valuestring, strlen(ProType_item->valuestring));
    }

    if (strlen(deviceType_item->valuestring) > sizeof(p->deviceType)) {
        *p_err_no = err_table[36];
        return -3;
    } else {
        memcpy(p->deviceType, deviceType_item->valuestring, strlen(deviceType_item->valuestring));
    }

    if (strlen(isReport_item->valuestring) > sizeof(p->isReport)) {
        *p_err_no = err_table[37];
        return -3;
    } else {
        memcpy(p->isReport, isReport_item->valuestring, strlen(isReport_item->valuestring));
    }

    if (strlen(nodeID_item->valuestring) > sizeof(p->nodeID)) {
        *p_err_no = err_table[38];
        return -3;
    } else {
        memcpy(p->nodeID, nodeID_item->valuestring, strlen(nodeID_item->valuestring));
    }

    if (strlen(productID_item->valuestring) > sizeof(p->productID)) {
        *p_err_no = err_table[39];
        return -3;
    } else {
        memcpy(p->productID, productID_item->valuestring, strlen(productID_item->valuestring));
    }

    if (strlen(guid) > sizeof(p->guid)) {
        return -3;
    } else {
        memcpy(p->guid, guid, strlen(guid));
    }

    if ((strlen(guid) + strlen(model_item->valuestring)) > sizeof(p->dev)) {
        return -3;
    } else {
        sprintf(p->dev, "%s_%s", model_item->valuestring, guid);
    }

    if (strlen(app) > sizeof(p->appname)) {
        return -3;
    } else {
        memcpy(p->appname, app, strlen(app));
    }

    return 0;
}


/**
  * @brief  设备/APP 注册 处理
  * @param  null
  * @retval null
  */
int topic_set_register(char* app, char *tpc, char *msg, char *response)
{
    char table_name[128];
    char guid[128];
    struct dev_property_s d_p;
    cJSON *jn = NULL;
    cJSON *token_item = NULL;
    cJSON *time_item = NULL;
    cJSON *body_item = NULL;
    cJSON *Array = NULL;
    int i;
    int size = 0;
    int res = 0;
    int rtn = 0;
    char *err_no = NULL;

    log_printf(MSG_VERBOSE, "%s", __FUNCTION__);

    if ((NULL == app) || (NULL == msg) || (NULL == response)) {
        rtn = -1;
        goto CLEAN_UP;
    }

    /* 解析请求的json数据 */
    jn = cJSON_Parse(msg);

    token_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TOKEN);
    if ((0 == token_item) || (0 == token_item->valuestring)) {
        err_no = err_table[0];
        rtn = -2;
        goto CLEAN_UP;
    }
    //printf("token_item = %s \n", token_item->valuestring);

    time_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TIMESTAMP);
    if ((0 == time_item) || (0 == time_item->valuestring)) {
        err_no = err_table[1];
        rtn = -2;
        goto CLEAN_UP;
    }
    //printf("time_item = %s \n", time_item->valuestring);

    body_item = cJSON_GetObjectItemCaseSensitive(jn, STR_BODY);
    if (0 == body_item) {
        err_no = err_table[2];
        rtn = -2;
        goto CLEAN_UP;
    }
    size = cJSON_GetArraySize(body_item);
    //printf("func(%s) cJSON_GetArraySize = %d \n", __FUNCTION__, size);
    if (0 == size) {
        err_no = err_table[2];
        rtn = -2;
        goto CLEAN_UP;
    }

    Array =  cJSON_CreateArray();
    for (i = 0; i < size; i ++) {
        cJSON *item = cJSON_GetArrayItem(body_item, i);
        if (0 == item) {
            continue;
        }

        cJSON *model_item = cJSON_GetObjectItemCaseSensitive(item, STR_MODEL);
        if ((0 == model_item) || (0 == model_item->valuestring)) {
            LOG_PRINTF_ERROR( "func(%s) model not found or model is NULL", __FUNCTION__);
            mqtt_response_ok_failure(1, response, app, token_item->valuestring);
            err_no = err_table[3];
            rtn = -2;
            goto CLEAN_UP;
        }
        //printf("model_item = %s \n", model_item->valuestring);
        memset(table_name, 0x00, sizeof(table_name));
        sprintf(table_name, "%s%s", STR_MODE_, model_item->valuestring);
        //printf("check %s table exist or not \n", table_name);
        /* 查询模型是否已经注册了 */
        res = table_exist_check(table_name);
        if (0 == res) {
            /* 模型没有创建不允许注册设备 */
            LOG_PRINTF_ERROR( "func(%s) regist model[%s] before regist dev, app[%s]", __FUNCTION__, table_name, app);
            mqtt_response_ok_failure(1, response, app, token_item->valuestring);
            err_no = err_table[40];
            rtn = -3;
            goto CLEAN_UP;
        }

        memset(table_name, 0x00, sizeof(table_name));
        sprintf(table_name, "%s_%s", STR_DEV, model_item->valuestring);
        //printf("check %s table exist or not \n", table_name);
        /* 设备表没有创建就创建 */
        creat_table_if_not_exist(DEV_TABLE_STRUCT, table_name);

        memset(&d_p, 0x00, sizeof(struct dev_property_s));
        /* 获取一个空闲的guid */
        memset(guid, 0x00, sizeof(guid));
        get_unused_guid(table_name, guid);
        log_printf(MSG_INFO, "table_name[%s] unused guid[%s]", table_name, guid);

        res = get_proerty_of_dev(guid, app, item, &d_p, &err_no);
        if (0 != res) {
            LOG_PRINTF_ERROR( "func(%s) get_proerty_of_dev error", __FUNCTION__);
            mqtt_response_ok_failure(1, response, app, token_item->valuestring);
            rtn = -4;
            goto CLEAN_UP;
        }

        /* 保存到设备表中 */
        res = save_property_to_dev_table(table_name, &d_p);
        if (0 != res) {
            LOG_PRINTF_ERROR( "func(%s) save_property_to_dev_table table[%s] error", __FUNCTION__, table_name);
            mqtt_response_ok_failure(1, response, app, token_item->valuestring);
            rtn = -5;
            goto CLEAN_UP;
        }

        char dev_name[128];
        memset(dev_name, 0x00, sizeof(dev_name));
        sprintf(dev_name, "%s_%s", model_item->valuestring, guid);
        /* 在内存增加新注册设备的设备信息和设备实时数据 */
        update_model_list_when_dev_regist_unregist(model_item->valuestring, dev_name, 1);

        /* 准备 设备/APP 注册通知 所需要的数据 */
        cJSON *temp_jn = cJSON_CreateObject();
        cJSON_AddItemToObject(temp_jn, STR_MODEL, cJSON_CreateString(d_p.model));
        cJSON_AddItemToObject(temp_jn, STR_PORT, cJSON_CreateString(d_p.port));
        cJSON_AddItemToObject(temp_jn, STR_ADDR, cJSON_CreateString(d_p.addr));
        cJSON_AddItemToObject(temp_jn, STR_DESC, cJSON_CreateString(d_p.desc));
        cJSON_AddItemToObject(temp_jn, STR_GUID, cJSON_CreateString(d_p.guid));
        cJSON_AddItemToObject(temp_jn, STR_DEV, cJSON_CreateString(d_p.dev));
        cJSON_AddItemToArray(Array, temp_jn);
    }

    /* 回复成功 */
    mqtt_response_ok_failure(0, response, app, token_item->valuestring);
    log_printf(MSG_INFO, "func(%s) add dev[%s] ok", __FUNCTION__, d_p.dev);

    /* 设备/APP 注册成功后，还需要进行设备/APP 注册通知 */
    topic_update_register_unregister(UPDATE_REGIST, Array);

CLEAN_UP:
    mqtt_error_ack(err_no, token_item, response, app);

    if (jn) cJSON_Delete(jn);

    return rtn;
}

/**
  * @brief  设备/APP 注册 处理
  * @param  null
  * @retval null
  */
int topic_set_register_V2(char* app, char *tpc, char *msg, char *response)
{
    char table_name[128];
    char guid[128];
    struct dev_property_s d_p;
    cJSON *jn = NULL;
    cJSON *token_item = NULL;
    cJSON *time_item = NULL;
    cJSON *body_item = NULL;
    cJSON *Array = NULL;
    int i;
    int size = 0;
    int res = 0;
    int rtn = 0;
    char *err_no = NULL;

    log_printf(MSG_VERBOSE, "%s", __FUNCTION__);

    if ((NULL == app) || (NULL == msg) || (NULL == response)) {
        rtn = -1;
        goto CLEAN_UP;
    }

    /* 解析请求的json数据 */
    jn = cJSON_Parse(msg);

    token_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TOKEN);
    if ((0 == token_item) || (0 == token_item->valuestring)) {
        err_no = err_table[0];
        rtn = -2;
        goto CLEAN_UP;
    }
    //printf("token_item = %s \n", token_item->valuestring);

    time_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TIMESTAMP);
    if ((0 == time_item) || (0 == time_item->valuestring)) {
        err_no = err_table[1];
        rtn = -2;
        goto CLEAN_UP;
    }
    //printf("time_item = %s \n", time_item->valuestring);

    body_item = cJSON_GetObjectItemCaseSensitive(jn, STR_BODY);
    if (0 == body_item) {
        err_no = err_table[2];
        rtn = -2;
        goto CLEAN_UP;
    }
    size = cJSON_GetArraySize(body_item);
    //printf("func(%s) cJSON_GetArraySize = %d \n", __FUNCTION__, size);
    if (0 == size) {
        err_no = err_table[2];
        rtn = -2;
        goto CLEAN_UP;
    }

    Array =  cJSON_CreateArray();
    for (i = 0; i < size; i ++) {
        cJSON *item = cJSON_GetArrayItem(body_item, i);
        if (0 == item) {
            continue;
        }

        cJSON *model_item = cJSON_GetObjectItemCaseSensitive(item, STR_MODEL);
        if ((0 == model_item) || (0 == model_item->valuestring)) {
            LOG_PRINTF_ERROR( "func(%s) model not found or model is NULL", __FUNCTION__);
            mqtt_response_ok_failure(1, response, app, token_item->valuestring);
            err_no = err_table[3];
            rtn = -2;
            goto CLEAN_UP;
        }
        //printf("model_item = %s \n", model_item->valuestring);
        memset(table_name, 0x00, sizeof(table_name));
        sprintf(table_name, "%s%s", STR_MODE_, model_item->valuestring);
        //printf("check %s table exist or not \n", table_name);
        /* 查询模型是否已经注册了 */
        res = table_exist_check(table_name);
        if (0 == res) {
            /* 模型没有创建不允许注册设备 */
            LOG_PRINTF_ERROR( "func(%s) regist model[%s] before regist dev, app[%s]", __FUNCTION__, table_name, app);
            mqtt_response_ok_failure(1, response, app, token_item->valuestring);
            err_no = err_table[40];
            rtn = -3;
            goto CLEAN_UP;
        }

        memset(table_name, 0x00, sizeof(table_name));
        sprintf(table_name, "%s_%s", STR_DEV, model_item->valuestring);
        //printf("check %s table exist or not \n", table_name);
        /* 设备表没有创建就创建 */
        creat_table_if_not_exist(DEV_TABLE_STRUCT, table_name);

        memset(&d_p, 0x00, sizeof(struct dev_property_s));
        /* 获取一个空闲的guid */
        memset(guid, '\0', sizeof(guid));
        res = get_unused_guid_V2(table_name, guid);
        if (res == -1)
        {
            LOG_PRINTF_ERROR( "func(%s) error", __FUNCTION__);
            mqtt_response_ok_failure(1, response, app, token_item->valuestring);
            rtn = -4;
            goto CLEAN_UP;
        }
        
        log_printf(MSG_INFO, "table_name[%s] unused guid[%s]", table_name, guid);

        res = get_proerty_of_dev(guid, app, item, &d_p, &err_no);
        if (0 != res) {
            LOG_PRINTF_ERROR( "func(%s) get_proerty_of_dev error", __FUNCTION__);
            mqtt_response_ok_failure(1, response, app, token_item->valuestring);
            rtn = -4;
            goto CLEAN_UP;
        }

        /* 保存到设备表中 */
        res = save_property_to_dev_table_V2(table_name, &d_p);
        if (0 != res) {
            LOG_PRINTF_ERROR( "func(%s) save_property_to_dev_table table[%s] error", __FUNCTION__, table_name);
            mqtt_response_ok_failure(1, response, app, token_item->valuestring);
            rtn = -5;
            goto CLEAN_UP;
        }

        char dev_name[128];
        memset(dev_name, 0x00, sizeof(dev_name));
        sprintf(dev_name, "%s_%s", model_item->valuestring, guid);
        /* 在内存增加新注册设备的设备信息和设备实时数据 */
        update_model_list_when_dev_regist_unregist(model_item->valuestring, dev_name, 1);

        /* 准备 设备/APP 注册通知 所需要的数据 */
        cJSON *temp_jn = cJSON_CreateObject();
        cJSON_AddItemToObject(temp_jn, STR_MODEL, cJSON_CreateString(d_p.model));
        cJSON_AddItemToObject(temp_jn, STR_PORT, cJSON_CreateString(d_p.port));
        cJSON_AddItemToObject(temp_jn, STR_ADDR, cJSON_CreateString(d_p.addr));
        cJSON_AddItemToObject(temp_jn, STR_DESC, cJSON_CreateString(d_p.desc));
        cJSON_AddItemToObject(temp_jn, STR_GUID, cJSON_CreateString(d_p.guid));
        cJSON_AddItemToObject(temp_jn, STR_DEV, cJSON_CreateString(d_p.dev));
        cJSON_AddItemToArray(Array, temp_jn);
    }

    // test begin
    // print_jn_realtime_data();
    // test end

    /* 回复成功 */
    mqtt_response_ok_failure(0, response, app, token_item->valuestring);
    log_printf(MSG_INFO, "func(%s) add dev[%s] ok", __FUNCTION__, d_p.dev);

    /* 设备/APP 注册成功后，还需要进行设备/APP 注册通知 */
    topic_update_register_unregister(UPDATE_REGIST, Array);

CLEAN_UP:
    mqtt_error_ack(err_no, token_item, response, app);

    if (jn) cJSON_Delete(jn);

    return rtn;
}


/**
  * @brief  获取某个模型注册的所有设备
  * @param  null
  * @retval null
  */
int get_model_regist_dev(char *model_name, cJSON *Array)
{
    int j;
    int column = 0;
    char table_name[128];

    if ((NULL == model_name) || (NULL == Array)) return -1;

    memset(table_name, 0x00, sizeof(table_name));
    sprintf(table_name, "%s%s", STR_DEV_, model_name);

    if (0 == table_exist_check(table_name)) return -2;

    get_table_column(table_name, &column);  /* 获取当前模型下有几个设备 */
    //printf("%s column = %d \n", table_name, column);
    for (j = 0; j < column; j++) {
        cJSON *object = cJSON_CreateObject();
        cJSON_AddItemToObject(object, STR_MODEL, cJSON_CreateString(model_name));

        cJSON *Array1 = cJSON_CreateArray();
        /* 从数据库获取设备/APP注册关系 */
        get_dev_app_regist_info(table_name, j, Array1);

        cJSON *item = cJSON_GetArrayItem(Array1, 0);
        cJSON *body_item = cJSON_DetachItemFromObject(item, STR_PORT);
        cJSON_AddItemToObject(object, STR_PORT, body_item);

        cJSON_AddItemToObject(object, STR_BODY, Array1);
        cJSON_AddItemToArray(Array, object);
    }

    return 0;
}


/**
  * @brief  设备/APP 注册关系查询 处理
  * @param  null
  * @retval null
  */
int topic_get_register(char* app, char *tpc, char *msg, char *response)
{
    // char topic[512];
    cJSON *jn = NULL;
    cJSON *token_item = NULL;
    cJSON *time_item = NULL;
    cJSON *body_item = NULL;
    cJSON *r_jn = NULL;
    cJSON *Array = NULL;
    int i;
    int size = 0;
    int rtn = 0;
    char *err_no = NULL;

    log_printf(MSG_VERBOSE, "%s", __FUNCTION__);

    if ((NULL == app) || (NULL == msg) || (NULL == response)) {
        rtn = -1;
        goto CLEAN_UP;
    }

    /* 解析请求的json数据 */
    jn = cJSON_Parse(msg);

    token_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TOKEN);
    if ((0 == token_item) || (0 == token_item->valuestring)) {
        err_no = err_table[0];
        rtn = -2;
        goto CLEAN_UP;
    }
    //printf("token_item = %s \n", token_item->valuestring);

    time_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TIMESTAMP);
    if ((0 == time_item) || (0 == time_item->valuestring)) {
        err_no = err_table[1];
        rtn = -2;
        goto CLEAN_UP;
    }
    //printf("time_item = %s \n", time_item->valuestring);

    body_item = cJSON_GetObjectItemCaseSensitive(jn, STR_BODY);
    if (0 == body_item) {
        err_no = err_table[2];
        rtn = -2;
        goto CLEAN_UP;
    }
    size = cJSON_GetArraySize(body_item);
    //printf("func(%s) cJSON_GetArraySize = %d \n", __FUNCTION__, size);

    Array = cJSON_CreateArray();
    if (0 == size) {
        /* body里面为空表示查询所有 */
        int count = get_model_count();
        int c1;
        char name[128];
        for (c1 = 0; c1 < count; c1++) {
            memset(name, 0x00, sizeof(name));
            get_model_name_by_index(c1, name);
            get_model_regist_dev(name, Array);
        }
    } else {
        /* 按body中的模型查询 */
        for (i = 0; i < size; i++) {    /* 循环实现查询多个模型 */
            cJSON * mode_item = cJSON_GetArrayItem(body_item, i);
            if ((0 == mode_item) || (0 == mode_item->valuestring)) {
                continue;
            }
            //printf("model_name = %s \n", mode_item->valuestring);

            get_model_regist_dev(mode_item->valuestring, Array);
        }
    }

    /* 准备回复的数据-------------------------------------------------------- */
    r_jn =  cJSON_CreateObject();
    mqtt_ack_msg_prepare(r_jn, token_item->valuestring);

    cJSON_AddItemToObject(r_jn, STR_BODY, Array);

    if (mqtt_ack_msg_send(r_jn, response, app)) {
        rtn = -3;
        goto CLEAN_UP;
    }

CLEAN_UP:
    mqtt_error_ack(err_no, token_item, response, app);

    if (jn) cJSON_Delete(jn);
    if (r_jn) cJSON_Delete(r_jn);

    return rtn;
}


/**
  * @brief  设备/APP 取消注册 处理
  * @param  null
  * @retval null
  */
int topic_action_unregister(char* app, char *tpc, char *msg, char *response)
{
    char table_name[128];
    char guid[64];
    char dev_name[128];
    cJSON *jn = NULL;
    cJSON *token_item = NULL;
    cJSON *time_item = NULL;
    cJSON *body_item = NULL;
    cJSON *Array = NULL;
    int i;
    int size = 0;
    int res = 0;
    int rtn = 0;
    char *err_no = NULL;

    log_printf(MSG_VERBOSE, "%s", __FUNCTION__);

    if ((NULL == app) || (NULL == msg) || (NULL == response)) {
        rtn = -1;
        goto CLEAN_UP;
    }

    /* 解析请求的json数据 */
    jn = cJSON_Parse(msg);

    token_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TOKEN);
    if ((0 == token_item) || (0 == token_item->valuestring)) {
        err_no = err_table[0];
        rtn = -2;
        goto CLEAN_UP;
    }
    //printf("token_item = %s \n", token_item->valuestring);

    time_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TIMESTAMP);
    if ((0 == time_item) || (0 == time_item->valuestring)) {
        err_no = err_table[1];
        rtn = -2;
        goto CLEAN_UP;
    }
    //printf("time_item = %s \n", time_item->valuestring);

    body_item = cJSON_GetObjectItemCaseSensitive(jn, STR_BODY);
    if (0 == body_item) {
        err_no = err_table[2];
        rtn = -2;
        goto CLEAN_UP;
    }
    size = cJSON_GetArraySize(body_item);
    //printf("func(%s) cJSON_GetArraySize = %d \n", __FUNCTION__, size);
    if (0 == size) {
        err_no = err_table[2];
        rtn = -2;
        goto CLEAN_UP;
    }

    Array =  cJSON_CreateArray();
    for (i = 0; i < size; i ++) {
        cJSON *item = cJSON_GetArrayItem(body_item, i);
        if (0 == item) {
            continue;
        }

        cJSON *model_item = cJSON_GetObjectItemCaseSensitive(item, STR_MODEL);
        if ((0 == model_item) || (0 == model_item->valuestring)) {
            err_no = err_table[3];
            rtn = -2;
            goto CLEAN_UP;
        }
        //printf("model_item = %s \n", model_item->valuestring);

        cJSON *port_item = cJSON_GetObjectItemCaseSensitive(item, STR_PORT);
        if ((0 == port_item) || (0 == port_item->valuestring)) {
            err_no = err_table[41];
            rtn = -2;
            goto CLEAN_UP;
        }
        //printf("port_item = %s \n", port_item->valuestring);

        cJSON *addr_item = cJSON_GetObjectItemCaseSensitive(item, STR_ADDR);
        if ((0 == addr_item) || (0 == addr_item->valuestring)) {
            err_no = err_table[42];
            rtn = -2;
            goto CLEAN_UP;
        }
        //printf("addr_item = %s \n", addr_item->valuestring);

        cJSON *desc_item = cJSON_GetObjectItemCaseSensitive(item, STR_DESC);
        if ((0 == desc_item) || (0 == desc_item->valuestring)) {
            err_no = err_table[43];
            rtn = -2;
            goto CLEAN_UP;
        }
        //printf("desc_item = %s \n", desc_item->valuestring);

        memset(table_name, 0x00, sizeof(table_name));
        sprintf(table_name, "%s_%s", STR_DEV, model_item->valuestring);
        //printf("check %s table exist or not \n", table_name);
        res = table_exist_check(table_name);
        if (0 == res) {
            /* 设备表不存在 */
            LOG_PRINTF_ERROR( "func(%s) dev_table[%s] null, app[%s]", __FUNCTION__, table_name, app);
            mqtt_response_ok_failure(1, response, app, token_item->valuestring);
            err_no = err_table[44];
            rtn = -3;
            goto CLEAN_UP;
        }

        memset(guid, 0x00, sizeof(guid));
        get_dev_guid(table_name, model_item->valuestring, port_item->valuestring, addr_item->valuestring, desc_item->valuestring, guid);
        if (0 == strlen(guid)) {
            LOG_PRINTF_ERROR( "func(%s) dev_table[%s] guid not found, app[%s]", __FUNCTION__, table_name, app);
            mqtt_response_ok_failure(1, response, app, token_item->valuestring);
            err_no = err_table[45];
            rtn = -4;
            goto CLEAN_UP;
        }
        memset(dev_name, 0x00, sizeof(dev_name));
        sprintf(dev_name, "%s_%s", model_item->valuestring, guid);

        /* 从设备表中删除设备 */
        delete_table_one_item(table_name, STR_DEV, dev_name);

        char dev_name[128];
        memset(dev_name, 0x00, sizeof(dev_name));
        sprintf(dev_name, "%s_%s", model_item->valuestring, guid);
        /* 在内存删除取消注册设备的设备信息和设备实时数据 */
        update_model_list_when_dev_regist_unregist(model_item->valuestring, dev_name, 0);

        /* 准备 设备/APP 注册通知 所需要的数据 */
        cJSON *temp_jn = cJSON_CreateObject();
        cJSON_AddItemToObject(temp_jn, STR_MODEL, cJSON_CreateString(model_item->valuestring));
        cJSON_AddItemToObject(temp_jn, STR_PORT, cJSON_CreateString(port_item->valuestring));
        cJSON_AddItemToObject(temp_jn, STR_ADDR, cJSON_CreateString(addr_item->valuestring));
        cJSON_AddItemToObject(temp_jn, STR_DESC, cJSON_CreateString(desc_item->valuestring));
        cJSON_AddItemToObject(temp_jn, STR_GUID, cJSON_CreateString(guid));
        cJSON_AddItemToObject(temp_jn, STR_DEV, cJSON_CreateString(dev_name));
        cJSON_AddItemToArray(Array, temp_jn);

		log_printf(MSG_INFO, "func(%s) delete dev[%s] ok", __FUNCTION__, dev_name);
    }

    /* 回复成功 */
    mqtt_response_ok_failure(0, response, app, token_item->valuestring);

    /* 设备/APP 注册成功后，还需要进行设备/APP 注册通知 */
    topic_update_register_unregister(UPDATE_UNREGIST, Array);

CLEAN_UP:
    mqtt_error_ack(err_no, token_item, response, app);

    if (jn) cJSON_Delete(jn);

    return rtn;
}


/**
  * @brief  从topic获取设备类型和设备标识，如：{app}/notify/event/datacenter/{设备类型}/{设备标识}
  * @param  null
  * @retval null
  */
int get_model_and_dev(char *request, char *model, char *dev)
{
    // int i;
    // int num;
    char *p = NULL;
    char *p1 = NULL;
    char *key = "/notify/event/datacenter/";

    if ((NULL == request) || (NULL == model) || (NULL == dev)) return -1;

    p = strstr(request, key);
    if (NULL == p) return -2;

    p += strlen(key);
    //printf("p = %s \n", p);

    p1 = strstr(p, "/");
    if (NULL == p1) return -2;

    memcpy(model, p, p1 - p);

    p1 += 1;
    //printf("p1 = %s \n", p1);
    memcpy(dev, p1, strlen(p1));

    return 0;
}


/**
  * @brief  实时数据写 处理
  * @param  null
  * @retval null
  */
int save_realtime_data(cJSON *jn, char *model, char *dev, char **p_err_no, char *err_buff)
{
    // cJSON *token_item = NULL;
    // cJSON *time_item = NULL;
    cJSON *body_item = NULL;
    int i = 0;
    int size = 0;
    int rtn = 0;

    log_printf(MSG_VERBOSE, "%s", __FUNCTION__);

    if ((NULL == jn) || (NULL == model) || (NULL == dev) || (NULL == p_err_no)) {
        return -1;
    }

    body_item = cJSON_GetObjectItemCaseSensitive(jn, STR_BODY);
    if (0 == body_item) {
        *p_err_no = err_table[2];
        return -2;
    }
    size = cJSON_GetArraySize(body_item);
    //printf("func(%s) cJSON_GetArraySize = %d \n", __FUNCTION__, size);
    if (0 == size) {
        *p_err_no = err_table[2];
        return -2;
    }

    for (i = 0; i < size; i++) {
        cJSON * array_item = cJSON_GetArrayItem(body_item, i);

        cJSON *name_item = cJSON_GetObjectItemCaseSensitive(array_item, STR_NAME);
        if ((0 == name_item) || (0 == name_item->valuestring)) {
            continue;
        }
        //printf("name_item = %s \n", name_item->valuestring);

        cJSON *val_item = cJSON_GetObjectItemCaseSensitive(array_item, STR_VAL);
        if ((0 == val_item) || (0 == val_item->valuestring)) {
            continue;
        }
        //printf("val_item = %s \n", val_item->valuestring);

        cJSON *quality_item = cJSON_GetObjectItemCaseSensitive(array_item, STR_QUALITY);
        if (0 == quality_item) {
            continue;
        }
        //printf("quality_item = %s \n", quality_item->valuestring);

        cJSON *index_item = cJSON_GetObjectItemCaseSensitive(array_item, STR_INDEX);
        if (0 == index_item) {
            //continue; //index字段为可选
        }
        //printf("index_item = %s \n", index_item->valuestring);

        cJSON *timestamp_item = cJSON_GetObjectItemCaseSensitive(array_item, STR_TIMESTAMP);
        if ((0 == timestamp_item) || (0 == timestamp_item->valuestring)) {
            continue;
        }
        //printf("timestamp_item = %s \n", timestamp_item->valuestring);

        if (NULL != index_item) {
            rtn = save_dev_realtime_data(model, dev, name_item->valuestring, val_item->valuestring, quality_item->valuestring, index_item->valuestring, timestamp_item->valuestring);
        } else {
            rtn = save_dev_realtime_data(model, dev, name_item->valuestring, val_item->valuestring, quality_item->valuestring, NULL, timestamp_item->valuestring);
        }
        if (0 != rtn) {
            /* 错误 */
            sprintf(err_buff, "func(%s) save model[%s] dev[%s] name[%s] error[%d] !", __FUNCTION__, model, dev, name_item->valuestring, rtn);
            *p_err_no = err_buff;
            LOG_PRINTF_ERROR( err_buff);
            return -3;
        }
    }

    return 0;
}


/**
  * @brief  查询表中是否有某个属性key的值为val 回调函数
  * @param  t_name 表名
  * @retval 1表示存在，0表示不存在
  */
static int table_have_property_cb(void *para, int argc, char **argv, char **azColName)
{
    // int i;
    int *exist = (int *)para;

    if (NULL == para) return 0;

    *exist = 1;

#if 0   /* 调试 */
    printf("column = %d \n", argc);
    for(i = 0; i < argc; i++){
        printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    }
#endif

    return 0;
}


/**
  * @brief  查询表中是否有某个属性key的值为val
  * @param  table 表名
  * @param  key 属性
  * @param  val 值
  * @retval 1表示有属性key的值为val，0表示没有
  */
int table_have_property(char *table, char *key, char *val)
{
    char str_sql[512];
    // int rtn;
    int para = 0;

    if ((NULL == table) || (NULL == key) || (NULL == val)) return -1;

    memset(str_sql, 0x00, sizeof(str_sql));
    sprintf(str_sql, SELECT_TABLE_PROPERTY, "*", table, key, val);
    db_select_with_cb(str_sql, table_have_property_cb, &para);
    // rtn = db_select_with_cb(str_sql, table_have_property_cb, &para);

    return para;
}


/**
  * @brief  查询模型和设备是否注册
  * @param  model 模型名
  * @param  model 设备名
  * @retval 1表示查询模型和设备都已经注册
  */
int model_and_dev_regist_check(char *model, char *dev)
{
    char buff[512];

    if ((NULL == model) || (NULL == dev)) return 0;

    //printf("model = %s, dev = %s \n", model, dev);

    memset(buff, 0x00, sizeof(buff));
    sprintf(buff, "mode_%s", model);
    if (0 == table_exist_check(buff)) {
        /* 模型没有注册 */
        LOG_PRINTF_ERROR( "func(%s) model[%s] not registed", __FUNCTION__, model);
        return 0;
    }

    memset(buff, 0x00, sizeof(buff));
    sprintf(buff, "dev_%s", model);
    if (0 == table_exist_check(buff)) {
        /* 没有设备表 */
        LOG_PRINTF_ERROR( "func(%s) table[%s] not exist", __FUNCTION__, buff);
        return 0;
    }

    /* 查询设备表中是否有dev设备 */
    if (0 == table_have_property(buff, STR_DEV, dev)) {
        LOG_PRINTF_ERROR( "func(%s) dev[%s] not registed", __FUNCTION__, dev);
        return 0;
    }

    return 1;
}


/**
  * @brief  查询模型是否注册
  * @param  model 模型名
  * @retval 1表示模型已经注册
  */
int model_regist_check(char *model)
{
    char buff[512];

    if (NULL == model) return 0;

    //printf("model = %s, dev = %s \n", model, dev);

    memset(buff, 0x00, sizeof(buff));
    sprintf(buff, "mode_%s", model);
    if (0 == table_exist_check(buff)) {
        /* 模型没有注册 */
        LOG_PRINTF_ERROR( "func(%s) model[%s] not registed", __FUNCTION__, model);
        return 0;
    }

    return 1;
}

/**
  * @brief  更新子设备状态
  * @param  
  * @retval 
  */
int dev_status_update(cJSON *jn, char *model, char *dev, char **p_err_no)
{
    log_printf(MSG_DEBUG, "enter %s", __FUNCTION__);

    ll_t *list = model_list;
    struct model_list_node_s *p = NULL;

    if ((NULL == jn) || (NULL == model) || (NULL == dev) || (NULL == p_err_no))
    {
        *p_err_no = err_table[3];
        return -1;
    }

    cJSON *body_array = NULL;
    body_array = cJSON_GetObjectItemCaseSensitive(jn, STR_BODY);
    if ((!cJSON_IsArray(body_array)))
    {
        *p_err_no = err_table[2];
        return -2;
    }

    for (int i = 0; i < list->len; i++)
    {
        p = (struct model_list_node_s *)ll_get_n(list, i);
        if ((p) && (0 == memcmp(p->model_name, model, strlen(p->model_name))) && (strlen(p->model_name) == strlen(model)))
        {
            /* 模型找到 */
            log_printf(MSG_DEBUG, "func(%s) model[%s] found", __FUNCTION__, model);
            if (NULL != p->jn_realtime_data)
            {
                cJSON *jn_realtime_data_item = NULL;
                cJSON_ArrayForEach(jn_realtime_data_item, p->jn_realtime_data)
                {
                    if (NULL != jn_realtime_data_item)
                    {
                        cJSON *dev_item = cJSON_GetObjectItemCaseSensitive(jn_realtime_data_item, STR_DEV);
                        if ((0 == memcmp(dev_item->valuestring, dev, strlen(dev_item->valuestring))) && (strlen(dev_item->valuestring) == strlen(dev)))
                        {
                            // 设备找到
                            log_printf(MSG_DEBUG, "func(%s) model[%s] dev[%s] found", __FUNCTION__, model, dev);

                            // 如何没有 sub_device_status 数组就创建，有了就不创建
                            // 查找是否有"sub_device_status"的key
                            cJSON *status_array = cJSON_GetObjectItemCaseSensitive(jn_realtime_data_item, STR_SUB_DEVICE_STATUS);
                            if (!cJSON_IsArray(status_array)) {
                                
                                log_printf(MSG_DEBUG, "sub_device_status array not found, create new sub_device_status array!");
                                // 如果没有找到，创建一个新的object
                                status_array = cJSON_CreateArray();
                            }

                            // sub_device_status 数组找到
                            cJSON *body_item = NULL;
                            cJSON_ArrayForEach(body_item, body_array)
                            {
                                cJSON *status_object = cJSON_CreateObject();
                                cJSON *name = cJSON_GetObjectItem(body_item, STR_NAME);
                                if (cJSON_IsString(name) && (name->valuestring != NULL))
                                {
                                    cJSON_AddStringToObject(status_object, STR_NAME, name->valuestring);
                                }
                                else
                                {
                                    cJSON_AddStringToObject(status_object, STR_NAME, "NULL");
                                }

                                cJSON *val = cJSON_GetObjectItem(body_item, STR_VAL);
                                if (cJSON_IsString(val) && (val->valuestring != NULL))
                                {
                                    cJSON_AddStringToObject(status_object, STR_VAL, val->valuestring);
                                }
                                else
                                {
                                    cJSON_AddStringToObject(status_object, STR_VAL, "NULL");
                                }

                                cJSON *timestamp = cJSON_GetObjectItem(body_item, STR_TIMESTAMP);
                                if (cJSON_IsString(timestamp) && (timestamp->valuestring != NULL))
                                {
                                    cJSON_AddStringToObject(status_object, STR_TIMESTAMP, timestamp->valuestring);
                                }
                                else
                                {
                                    cJSON_AddStringToObject(status_object, STR_TIMESTAMP, "NULL");
                                }

                                int flag = 0;
                                cJSON *status_array_item = NULL;
                                cJSON_ArrayForEach(status_array_item, status_array)
                                {
                                    if (cJSON_IsObject(status_array_item))
                                    {
                                        cJSON *status_array_item_name = cJSON_GetObjectItem(status_array_item, "name");
                                        if (cJSON_IsString(status_array_item_name) &&
                                            (0 == memcmp(status_array_item_name->valuestring, name->valuestring, strlen(status_array_item_name->valuestring)))
                                            && (strlen(status_array_item_name->valuestring) == strlen(name->valuestring)))
                                        {
                                            flag = 1; // 找到匹配的键值对，表明已经存在，不需要再添加
                                            // 需要再次更新状态
                                            // 更新 val
                                            cJSON_DeleteItemFromObject(status_array_item, STR_VAL);
                                            cJSON *val = cJSON_GetObjectItem(body_item, STR_VAL);
                                            if (cJSON_IsString(val) && (val->valuestring != NULL))
                                            {
                                                cJSON_AddStringToObject(status_array_item, STR_VAL, val->valuestring);
                                            }
                                            else
                                            {
                                                cJSON_AddStringToObject(status_array_item, STR_VAL, "NULL");
                                            }

                                            // 更新 timestamp
                                            cJSON_DeleteItemFromObject(status_array_item, STR_TIMESTAMP);
                                            cJSON *timestamp = cJSON_GetObjectItem(body_item, STR_TIMESTAMP);
                                            if (cJSON_IsString(timestamp) && (timestamp->valuestring != NULL))
                                            {
                                                cJSON_AddStringToObject(status_array_item, STR_TIMESTAMP, timestamp->valuestring);
                                            }
                                            else
                                            {
                                                cJSON_AddStringToObject(status_array_item, STR_TIMESTAMP, "NULL");
                                            }
                                        }
                                    }
                                }

                                if (flag == 0)
                                {
                                    // 将status对象添加到status数组中
                                    cJSON_AddItemToArray(status_array, status_object);
                                    // 将status数组添加到dev_item中
                                    cJSON_AddItemToObject(jn_realtime_data_item, STR_SUB_DEVICE_STATUS, status_array);
                                }
#if 1 /* 调试 */
                                char *status_array_payload = cJSON_Print(status_array);
                                log_printf(MSG_DEBUG, "size is (%ld), sub_device_status = %s", strlen(status_array_payload), status_array_payload);
                                if (status_array_payload)
                                {
                                    cJSON_free(status_array_payload);
                                }
#endif
                            }
                        }
                    }
                }
            }
        }
    } // end of for (i = 0; i < list->len; i++)

    return 0;
}

/**
  * @brief  实时数据与冻结数据写 处理
  * @param  null
  * @retval null
  */
int topic_notify_event(char* app, char *tpc, char *msg, char *response)
{
    char model[128];
    char dev[128];
    char topic[512];
    char err_buff[256];
    cJSON *jn = NULL;
    cJSON *r_jn = NULL;
    cJSON *token_item = NULL;
    cJSON *time_item = NULL;
    cJSON *data_row_item = NULL;
    char *payload = NULL;
    int payload_len;
    int res = 0;
    int rtn = 0;
    char *err_no = NULL;

    log_printf(MSG_VERBOSE, "%s", __FUNCTION__);

    if ((NULL == app) || (NULL == msg) || (NULL == response)) {
        rtn = -1;
        goto CLEAN_UP;
    }

    memset(model, 0x00, sizeof(model));
    memset(dev, 0x00, sizeof(dev));
    get_model_and_dev(tpc, model, dev);
    //printf("model = %s, dev = %s \n", model, dev);

    /* 解析请求的json数据 */
    jn = cJSON_Parse(msg);

    token_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TOKEN);
    if ((0 == token_item) || (0 == token_item->valuestring)) {
        err_no = err_table[0];
        rtn = -2;
        goto CLEAN_UP;
    }
    //printf("token_item = %s \n", token_item->valuestring);

    time_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TIMESTAMP);
    if ((0 == time_item) || (0 == time_item->valuestring)) {
        err_no = err_table[1];
        rtn = -2;
        goto CLEAN_UP;
    }
    //printf("time_item = %s \n", time_item->valuestring);

    data_row_item = cJSON_GetObjectItemCaseSensitive(jn, STR_DATA_ROW);
    if ((0 == data_row_item) || (0 == data_row_item->valuestring)) {
        err_no = err_table[46];
        rtn = -2;
        goto CLEAN_UP;
    }
    //printf("data_row_item = %s \n", data_row_item->valuestring);

    /* 在未进行模型注册和设备注册时，进行数据存储，应不能正常存储 */
    if (1 != model_and_dev_regist_check(model, dev)) {
    // if (0) {   // test 为了测试方便，暂时不校验
        LOG_PRINTF_ERROR( "func(%s) regist model and dev before write realtime/frozen data", __FUNCTION__);
        err_no = err_table[48];
        res = -1;
    } else {
        if (0 == memcmp(data_row_item->valuestring, STR_SINGLE, strlen(STR_SINGLE)) && (strlen(data_row_item->valuestring) == strlen(STR_SINGLE))) {
            /* 实时数据 */
            memset(err_buff, 0x00, sizeof(err_buff));
            res = save_realtime_data(jn, model, dev, &err_no, err_buff);
        } else if((0 == memcmp(data_row_item->valuestring, STR_STATUS, strlen(STR_STATUS))) && (strlen(data_row_item->valuestring) == strlen(STR_STATUS))) {
            // 更新子设备状态
            res = dev_status_update(jn, model, dev, &err_no);
        } else {
            /* data_row错误 */
            err_no = err_table[47];
            res = -1;
        }
    }

    /* 准备回复的数据-------------------------------------------------------- */
    r_jn =  cJSON_CreateObject();
    mqtt_ack_msg_prepare(r_jn, token_item->valuestring);

    if (0 == res) {
        cJSON_AddItemToObject(r_jn, STR_STATUS, cJSON_CreateString(STR_OK));
    } else {
        cJSON_AddItemToObject(r_jn, STR_STATUS, cJSON_CreateString(STR_FAILURE));
    }

    if (db_config.json_unformatted) {
        payload = cJSON_PrintUnformatted(r_jn);
    } else {
        payload = cJSON_Print(r_jn);
    }
    payload_len = (int)strlen(payload);
    memset(topic, 0x00, sizeof(topic));
    sprintf(topic, response, app, model, dev);

    if (mqtt_publish_message(topic, payload, payload_len)) {
        //printf("Failed to publish message \n");
        rtn = -3;
        goto CLEAN_UP;
    }

CLEAN_UP:
    memset(topic, 0x00, sizeof(topic));
    sprintf(topic, response, "%s", model, dev);
    mqtt_error_ack(err_no, token_item, topic, app);

    if (jn) cJSON_Delete(jn);
    if (r_jn) cJSON_Delete(r_jn);
    if (payload) cJSON_free(payload);

    return rtn;
}


/**
  * @brief  按设备读取数据 处理
  * @param  null
  * @retval null
  */
int topic_get_realtime(char* app, char *tpc, char *msg, char *response)
{
    // char topic[512];
    cJSON *jn = NULL;
    cJSON *token_item = NULL;
    cJSON *time_item = NULL;
    cJSON *body_item = NULL;
    cJSON *r_jn = NULL;
    cJSON *Array = NULL;
    int i;
    int j;
    int size = 0;
    int rtn = 0;
    char *err_no = NULL;

    log_printf(MSG_VERBOSE, "%s", __FUNCTION__);

    if ((NULL == app) || (NULL == msg) || (NULL == response)) {
        rtn = -1;
        goto CLEAN_UP;
    }

    /* 解析请求的json数据 */
    jn = cJSON_Parse(msg);

    token_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TOKEN);
    if ((0 == token_item) || (0 == token_item->valuestring)) {
        err_no = err_table[0];
        rtn = -2;
        goto CLEAN_UP;
    }
    //printf("token_item = %s \n", token_item->valuestring);

    time_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TIMESTAMP);
    if ((0 == time_item) || (0 == time_item->valuestring)) {
        err_no = err_table[1];
        rtn = -2;
        goto CLEAN_UP;
    }
    //printf("time_item = %s \n", time_item->valuestring);

    body_item = cJSON_GetObjectItemCaseSensitive(jn, STR_BODY);
    if (0 == body_item) {
        err_no = err_table[2];
        rtn = -2;
        goto CLEAN_UP;
    }
    size = cJSON_GetArraySize(body_item);
    //printf("func(%s) cJSON_GetArraySize = %d \n", __FUNCTION__, size);
    if (0 == size) {
        err_no = err_table[2];
        rtn = -2;
        goto CLEAN_UP;
    }

    Array = cJSON_CreateArray();
    for (i = 0; i < size; i++) {
        cJSON *item = cJSON_GetArrayItem(body_item, i);
        if ((0 == item)) {
            continue;
        }

        cJSON *dev_item = cJSON_GetObjectItemCaseSensitive(item, STR_DEV);
        if ((0 == dev_item) || (0 == dev_item->valuestring)) {
            err_no = err_table[50];
            rtn = -2;
            goto CLEAN_UP;
        }
        //printf("dev_item = %s \n", dev_item->valuestring);
        char model[128];
        memset(model, 0x00, sizeof(model));
        char *p = strrchr(dev_item->valuestring, '_');
        if (p) {
            memcpy(model, dev_item->valuestring, strlen(dev_item->valuestring) - strlen(p));
            //printf("model = %s \n", model);
        }

        cJSON *totalcall_item = cJSON_GetObjectItemCaseSensitive(item, STR_TOTALCALL);
        if ((0 == totalcall_item) || (0 == totalcall_item->valuestring)) {
            err_no = err_table[51];
            rtn = -2;
            goto CLEAN_UP;
        }
        //printf("totalcall_item = %s \n", totalcall_item->valuestring);

        cJSON *Array1 = cJSON_CreateArray();
        if (0 == strcmp(totalcall_item->valuestring, "1")) {
            /* 设备全部实时数据获取 */
            get_one_dev_realtime_data(model, dev_item->valuestring, Array1);
        } else if (0 == strcmp(totalcall_item->valuestring, "0")) {
            /* 设备部分实时数据获取 */
            cJSON *body1_item = cJSON_GetObjectItemCaseSensitive(item, STR_BODY);
            if (0 == body1_item) {
                rtn = -2;
                goto CLEAN_UP;
            }
            int size1 = cJSON_GetArraySize(body1_item); /* 属性名个数 */
            if (0 == size1) {
                rtn = -2;
                goto CLEAN_UP;
            }
            for (j = 0; j < size1; j++) {   /* 多个属性循环查询 */
                cJSON *property_item = cJSON_GetArrayItem(body1_item, j);
                if ((NULL == property_item) || (NULL == property_item->valuestring)) {
                    continue;
                }
                /* 查询单个属性信息 */
                get_dev_one_realtime_data(model, dev_item->valuestring, property_item->valuestring, Array1);
            }
        } else {
            /* totalcall错误 */
            err_no = err_table[52];
            rtn = -2;
            goto CLEAN_UP;
        }

        cJSON *object = cJSON_CreateObject();
        cJSON_AddItemToObject(object, STR_DEV, cJSON_CreateString(dev_item->valuestring));
        cJSON_AddItemToObject(object, STR_BODY, Array1);
        cJSON_AddItemToArray(Array, object);
    }

    /* 准备回复的数据-------------------------------------------------------- */
    r_jn =  cJSON_CreateObject();
    mqtt_ack_msg_prepare(r_jn, token_item->valuestring);

    cJSON_AddItemToObject(r_jn, STR_BODY, Array);

    if (mqtt_ack_msg_send(r_jn, response, app)) {
        rtn = -3;
        goto CLEAN_UP;
    }

CLEAN_UP:
    mqtt_error_ack(err_no, token_item, response, app);

    if (jn) cJSON_Delete(jn);
    if (r_jn) cJSON_Delete(r_jn);

    return rtn;
}


/**
  * @brief  根据dev得到frozen_table_name，如LowVCircuitCtrl_1得到LowVCircuitCtrl_frozen_1
  * @param  null
  * @retval null
  */
int get_frozen_table_name_from_dev_name(char *dev, char *table_name)
{
    char *p = NULL;
    int i = 0;

    if ((NULL == dev) || (NULL == table_name)) return -1;

    p = strrchr(dev, '_');
    if (NULL == p) return -2;

    memcpy(&table_name[i], dev, p - dev);
    i += p - dev;
    memcpy(&table_name[i], "_frozen_", strlen("_frozen_"));
    i += strlen("_frozen_");
    p += 1;
    memcpy(&table_name[i], p, strlen(p));

    return 0;
}


struct frozen_type_t_s {
    char type[32];
    //int (*fun)(char* app, char *tpc, char *msg, char *response);   /* 处理函数 */
    int seconds;
    int num;        /* 比较位数 */
    int add_method; /* 0：如果是天、时、分的frozen_type就用加多少秒的方式，1：如果是月的frozen_type就直接在字符串上处理，主要解决有些月份天数不一致的问题 */
};

struct frozen_type_t_s frozen_type_t[] = {
    {"mon",         1,                  7,      1},
    {"day",         24 * 60 * 60,       10,     0},
    {"billday",     24 * 60 * 60,       10,     0},
    {"hour",        60 * 60,            13,     0},
    {"min",         60,                 16,     0},
    {"realtime",    60,                 19,     0}
};


/**
  * @brief  对frozen_type合法性检查
  * @param  null
  * @retval null
  */
int frozen_type_valid_check(char *frozen_type)
{
    int i;
    int flag = -1;

    if (NULL == frozen_type) return flag;

    for (i = 0; i < sizeof(frozen_type_t) / sizeof(struct frozen_type_t_s); i++) {
        if ((0 == memcmp(frozen_type_t[i].type, frozen_type, strlen(frozen_type))) && (strlen(frozen_type_t[i].type) == strlen(frozen_type))) {
            flag = i;
            break;
        }
    }

    return flag;
}


/**
  * @brief  字符串时间加多少秒得到新的字符串时间 "2019-03-01T08:05:00.000+0800"
  * @param  time 要运算的时间
  * @param  add_method 0：如果是天、时、分的frozen_type就用加多少秒的方式，1：如果是月的frozen_type就直接在字符串上处理，主要解决有些月份天数不一致的问题
  * @param  base 要运算的时间(add_method = 0 base就是多少秒，add_method = 1 base就是多少月)
  * @param  r_time 运算结果时间
  * @retval null
  */
int time_add(char *time, int add_method, int base, char *r_time)
{
    struct tm t_tm;
    int year;
    int mon;
    int day;
    int hour;
    int min;
    int sec;
    char buff[64];

    if ((NULL == time) || (NULL == r_time)) return -1;

    memset(buff, 0x00, sizeof(buff));
    memcpy(buff, time, 4);
    year = atoi(buff);

    memset(buff, 0x00, sizeof(buff));
    memcpy(buff, time + 5, 2);
    mon = atoi(buff);

    memset(buff, 0x00, sizeof(buff));
    memcpy(buff, time + 8, 2);
    day = atoi(buff);

    memset(buff, 0x00, sizeof(buff));
    memcpy(buff, time + 11, 2);
    hour = atoi(buff);

    memset(buff, 0x00, sizeof(buff));
    memcpy(buff, time + 14, 2);
    min = atoi(buff);

    memset(buff, 0x00, sizeof(buff));
    memcpy(buff, time + 17, 2);
    sec = atoi(buff);

    if (0 == add_method) {
        /* 按秒加 */
        t_tm.tm_year = year - 1900;
        t_tm.tm_mon = mon - 1;
        t_tm.tm_mday = day;
        t_tm.tm_hour = hour;
        t_tm.tm_min = min;
        t_tm.tm_sec = sec;
        t_tm.tm_isdst = -1;  /* Daylight saving time */
        //printf("%04d-%02d-%02d %02d:%02d:%02d \r\n", t_tm.tm_year + 1900, t_tm.tm_mon + 1, t_tm.tm_mday, t_tm.tm_hour, t_tm.tm_min, t_tm.tm_sec);

        time_t t1 = mktime(&t_tm);
        t1 += base;
        struct tm *ptm_ = localtime(&t1);
        //printf("%04d-%02d-%02d %02d:%02d:%02d \r\n", ptm_->tm_year + 1900, ptm_->tm_mon + 1, ptm_->tm_mday, ptm_->tm_hour, ptm_->tm_min, ptm_->tm_sec);

        ptm_->tm_year += 1900;
        ptm_->tm_mon += 1;
        sprintf(r_time, "%04d-%02d-%02dT%02d:%02d:%02d", ptm_->tm_year, ptm_->tm_mon, ptm_->tm_mday, ptm_->tm_hour, ptm_->tm_min, ptm_->tm_sec);
    } else {
        /* 按月加 */
        int mon1 = mon + base;
        int carry = mon1 / 13;
        mon = mon1 % 13;
        if (carry) {
            year += carry;
        }
        sprintf(r_time, "%04d-%02d-%02dT%02d:%02d:%02d", year, mon, day, hour, min, sec);
    }

    return 0;
}


/**
  * @brief  对历史记录查询中的属性进行过滤
  * @param  property 需要查询的属性
  * @param  object 过滤的对象
  * @retval null
  */
int frozen_data_property_filter(cJSON *property, cJSON *object)
{
    int size;
    int i;
    int size1;
    int j;
    int flag;

    if ((NULL == property) || (NULL == object)) return -1;

    size = cJSON_GetArraySize(object);
    //printf("func(%s) size = %d \n", __FUNCTION__, size);
    size1 = cJSON_GetArraySize(property);
    //printf("func(%s) size1 = %d \n", __FUNCTION__, size1);
    for (i = 0; i < size; i++) {
        cJSON *item = cJSON_GetArrayItem(object, i);
        if (NULL == item) {
            continue;
        }
        cJSON *name_item = cJSON_GetObjectItemCaseSensitive(item, STR_NAME);
        if ((0 == name_item) || (0 == name_item->valuestring)) {
            continue;
        }
        flag = 0;
        for (j = 0; j < size1; j++) {
            cJSON *item1 = cJSON_GetArrayItem(property, j);
            if ((0 == item1) || (0 == item1->valuestring)) {
                continue;
            }
            if ((0 == memcmp(name_item->valuestring, item1->valuestring, strlen(item1->valuestring))) && (strlen(name_item->valuestring) == strlen(item1->valuestring))) {
                /* 属性是需要的 */
                flag = 1;
                break;
            }
        }
        if (0 == flag) {
            /* 不需要的属性就删除 */
            cJSON_DeleteItemFromArray(object, i);
            log_printf(MSG_DEBUG, "cJSON_DeleteItemFromArray");
            i -= 1;
            size -= 1;
        }
    }

    return 0;
}


/**
  * @brief  对历史记录查询中的时间进行过滤
  * @param  start 起始时间
  * @param  start 结束时间
  * @param  num 比较位数
  * @param  time_span 多少个周期
  * @param  seconds 每个周期多少秒
  * @param  object 过滤对象
  * @param  array 过滤结果保存
  * @retval null
  */
int frozen_data_time_filter(char *start, char *end, int num, int time_span, int seconds, int add_method, cJSON *object, cJSON *array, char *time_type)
{
    char time[64];
    int sec = 0;
    int size;
    int i;
    int ret;
    int flag;
    cJSON *time_item;

    if ((NULL == start) || (NULL == end) || (0 == time_span) || (NULL == object) || (NULL == array) || (NULL == time_type)) return -1;

    ret = strncmp(start, end, num);
    if (ret >= 0) {
        /* 起始时间不能大于结束时间 */
        LOG_PRINTF_ERROR( "func(%s) start_time[%s] > end_time[%s] error", __FUNCTION__, start, end);
        return -2;
    }

#if 0   /* 调试 */
    char *p1 = cJSON_Print(object);
    printf("p1 = %s \n", p1);
#endif

    while(1) {
        memset(time, 0x00, sizeof(time));
        time_add(start, add_method, sec, time);
        sec += (time_span * seconds);
        if (0 <= strncmp(time, end, num)) {
            //printf("time[%s] >= end[%s], check done \n", time, end);
            break;
        }

        size = cJSON_GetArraySize(object);
        //printf("func(%s) size = %d \n", __FUNCTION__, size);
        flag = 0;
        cJSON *jn_body = cJSON_CreateArray();
        for (i = 0; i < size; i++) {
            cJSON *item = cJSON_GetArrayItem(object, i);
            if (NULL == item) {
                continue;
            }
            cJSON *timestartgather_item = cJSON_GetObjectItemCaseSensitive(item, STR_TIMESTARTGATHER);
            if ((0 == timestartgather_item) || (0 == timestartgather_item->valuestring)) {
                continue;
            }
            //printf("timestartgather_item = %s \n", timestartgather_item->valuestring);
            cJSON *timeendgather_item = cJSON_GetObjectItemCaseSensitive(item, STR_TIMEENDGATHER);
            if ((0 == timeendgather_item) || (0 == timeendgather_item->valuestring)) {
                continue;
            }
            //printf("timeendgather_item = %s \n", timeendgather_item->valuestring);
            cJSON *timestamp_item = cJSON_GetObjectItemCaseSensitive(item, STR_TIMESTAMP);
            if ((0 == timestamp_item) || (0 == timestamp_item->valuestring)) {
                continue;
            }
            //printf("timestamp_item = %s \n", timestamp_item->valuestring);
            if (0 == strcmp(time_type, STR_TIMESTARTGATHER)) {
                time_item = timestartgather_item;
            } else if (0 == strcmp(time_type, STR_TIMEENDGATHER)) {
                time_item = timeendgather_item;
            } else if (0 == strcmp(time_type, STR_TIMESTAMP)) {
                time_item = timestamp_item;
            } else {
                LOG_PRINTF_ERROR( "time_type = %s error", time_type);
                return -3;
            }

            cJSON *additionalcheck_item = cJSON_GetObjectItemCaseSensitive(item, STR_ADDITIONALCHECK);
            if ((0 == additionalcheck_item) || (0 == additionalcheck_item->valuestring)) {
                continue;
            }
            //printf("additionalcheck_item = %s \n", additionalcheck_item->valuestring);
            cJSON *name_item = cJSON_GetObjectItemCaseSensitive(item, STR_NAME);
            if ((0 == name_item) || (0 == name_item->valuestring)) {
                continue;
            }
            //printf("name_item = %s \n", name_item->valuestring);
            cJSON *val_item = cJSON_GetObjectItemCaseSensitive(item, STR_VALUE);
            if ((0 == val_item) || (0 == val_item->valuestring)) {
                continue;
            }
            //printf("val_item = %s \n", val_item->valuestring);
            if (0 == strncmp(time, time_item->valuestring, num)) {
                /* 找到一个符合要求的历史记录 */
                //log_printf(MSG_INFO, "func(%s) found frozen_data at time[%s], name = %s, value = %s", __FUNCTION__, timestamp_item->valuestring, name_item->valuestring, val_item->valuestring);
                #if 0   /* 调试 */
                char *p1 = cJSON_Print(item);
                printf("p1 = %s \n", p1);
                #endif
                if (0 == flag) {
                    cJSON *object1 = cJSON_CreateObject();
                    cJSON_AddItemToObject(object1, STR_TIMESTAMP, cJSON_CreateString(timestamp_item->valuestring));
                    cJSON_AddItemToObject(object1, STR_TIMESTARTGATHER, cJSON_CreateString(timestartgather_item->valuestring));
                    cJSON_AddItemToObject(object1, STR_TIMEENDGATHER, cJSON_CreateString(timeendgather_item->valuestring));
                    cJSON_AddItemToObject(object1, STR_ADDITIONALCHECK, cJSON_CreateString(additionalcheck_item->valuestring));
                    cJSON_AddItemToObject(object1, STR_BODY, jn_body);
                    cJSON_AddItemToArray(array, object1);
                    flag = 1;
                }
                cJSON *object2 = cJSON_CreateObject();
                cJSON_AddItemToObject(object2, STR_NAME, cJSON_CreateString(name_item->valuestring));
                cJSON_AddItemToObject(object2, STR_VAL, cJSON_CreateString(val_item->valuestring));
                cJSON_AddItemToArray(jn_body, object2);
            }
        }

        if (0 == flag) {
            /* 防止cJSON *jn_body = cJSON_CreateArray();申请内存了，又没有找到符合要求的历史记录，导致内存泄漏 */
            if (jn_body) cJSON_Delete(jn_body);
        }
    }

    return 0;
}


/**
  * @brief  对起始时间和结束时间的无效部分清零
  * @param  start_t 起始时间
  * @param  end_t 结束时间
  * @param  index 清零位置
  * @retval null
  */
static int time_clear(char *start_t, char *end_t, int index)
{
    char *t_zero = "0000-00-00T00:00:00.000";   /* 时区不清零 */
    int size;

    if ((NULL == start_t) || (NULL == end_t)) {
        return -1;
    }

    size = strlen(t_zero) - index;

    //printf("start_t = %s, end_t = %s \n", start_t, end_t);

    memcpy(&start_t[index], &t_zero[index], size);
    memcpy(&end_t[index], &t_zero[index], size);

    //printf("start_t = %s, end_t = %s \n", start_t, end_t);

    return 0;
}


/**
  * @brief  从数据库获取历史记录
  * @param  null
  * @retval null
  */
int get_frozen_data(char *table_name, char *frozen_type, int time_span, char *start, char *end, cJSON *property, cJSON *array, char *time_type)
{
    char start_t[32];
    char end_t[32];
    cJSON *object;
    int index;
    // int size;
    // int i;

    if ((NULL == table_name) || (NULL == frozen_type) || (NULL == start) || (NULL == end) || (NULL == property) || (NULL == array) || (NULL == time_type)) return -1;

    index = frozen_type_valid_check(frozen_type);
    if (0 > index) {
        return -2;
    }

    memset(start_t, 0x00, sizeof(start_t));
    memset(end_t, 0x00, sizeof(end_t));
    strcpy(start_t, start);
    strcpy(end_t, end);
    /* 对起始时间和结束时间的无效部分清零 */
    time_clear(start_t, end_t, frozen_type_t[index].num);

    object =  cJSON_CreateObject();
    /* 从数据库中读取frozen_type一样的所有数据，并且过滤属性名property */
    get_frozen_type_and_property_data(table_name, frozen_type, property, object, start_t, end_t, time_type);
#if 0
    char *p1 = cJSON_Print(object);
    printf("p1 = %s \n", p1);
#endif

    /* 过滤时间，并得到查询结果 */
    frozen_data_time_filter(start, end, frozen_type_t[index].num, time_span, frozen_type_t[index].seconds, frozen_type_t[index].add_method, object, array, time_type);

    if (object) cJSON_Delete(object);

    return 0;
}


/**
  * @brief  从数据库中读取event一样的soe事件 回调函数
  * @param  null
  * @retval null
  */
static int get_history_startNo_endNo_cb(void *para, int argc, char **argv, char **azColName)
{
    int i;
    cJSON *Array;
    cJSON *jn;

    if (NULL == para) return 0;

    Array = (cJSON *)para;
    jn =  cJSON_CreateObject();

    for(i = 0; i < argc; i++){
        cJSON_AddItemToObject(jn, azColName[i], cJSON_CreateString(argv[i]));
    }
    cJSON_AddItemToArray(Array, jn);

#if 0   /* 调试 */
    printf("column = %d \n", argc);
    for(i = 0; i < argc; i++){
        printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    }
#endif

    return 0;
}


/**
  * @brief  查询历史记录，第几条起始start，第几条结束end
  * @param  null
  * @retval null
  */
static int get_history_startNo_endNo(char *t_name, int start, int end, char *frozentype, char *key, char *val, cJSON *body)
{
    cJSON *jn;
    char str_sql[256];
    int size;
    // int rtn;

    if ((NULL == t_name) || (NULL == frozentype) || (NULL == key) || (NULL == val) || (NULL == body)) return -1;
    if ((start > end) || (start < 1) || (end < 1)) return -1;

    jn = cJSON_CreateArray();

    /* 查询数据库 */
    memset(str_sql, 0x00, sizeof(str_sql));
    sprintf(str_sql, SELECT_TABLE_FROZENTYPE_ITEM, "*", t_name, frozentype, key, val);
    //printf("func(%s) str_sql = %s \n", __FUNCTION__, str_sql);
    // rtn = db_select_with_cb(str_sql, get_history_startNo_endNo_cb, (void *)jn);
    db_select_with_cb(str_sql, get_history_startNo_endNo_cb, (void *)jn);

    size = cJSON_GetArraySize(jn);
    //printf("func(%s) cJSON_GetArraySize = %d \n", __FUNCTION__, size);

    if (size < end) {
        end = size;
    }

    log_printf(MSG_VERBOSE, "%s start[%d] end[%d]", __FUNCTION__, start, end);

    start = start - 1;  /* 因为cJSON_GetArrayItem函数是从0开始计数的 */
    end = end - 1;

    for (start = start; start <= end; start++) {
        cJSON * item = cJSON_GetArrayItem(jn, start);
        if (NULL == item) {
            continue;
        }
        //printf("item = %s \n", item->valuestring);

        cJSON *timestartgather_item = cJSON_GetObjectItemCaseSensitive(item, STR_TIMESTARTGATHER);
        if ((0 == timestartgather_item) || (0 == timestartgather_item->valuestring)) {
            continue;
        }
        //printf("timestartgather_item = %s \n", timestartgather_item->valuestring);
        cJSON *timeendgather_item = cJSON_GetObjectItemCaseSensitive(item, STR_TIMEENDGATHER);
        if ((0 == timeendgather_item) || (0 == timeendgather_item->valuestring)) {
            continue;
        }
        //printf("timeendgather_item = %s \n", timeendgather_item->valuestring);
        cJSON *timestamp_item = cJSON_GetObjectItemCaseSensitive(item, STR_TIMESTAMP);
        if ((0 == timestamp_item) || (0 == timestamp_item->valuestring)) {
            continue;
        }
        //printf("timestamp_item = %s \n", timestamp_item->valuestring);
        cJSON *additionalcheck_item = cJSON_GetObjectItemCaseSensitive(item, STR_ADDITIONALCHECK);
        if ((0 == additionalcheck_item) || (0 == additionalcheck_item->valuestring)) {
            continue;
        }
        //printf("additionalcheck_item = %s \n", additionalcheck_item->valuestring);
        cJSON *name_item = cJSON_GetObjectItemCaseSensitive(item, STR_NAME);
        if ((0 == name_item) || (0 == name_item->valuestring)) {
            continue;
        }
        //printf("name_item = %s \n", name_item->valuestring);
        cJSON *val_item = cJSON_GetObjectItemCaseSensitive(item, STR_VALUE);
        if ((0 == val_item) || (0 == val_item->valuestring)) {
            continue;
        }
        //printf("val_item = %s \n", val_item->valuestring);

        cJSON *object1 = cJSON_CreateObject();
        cJSON_AddItemToObject(object1, STR_TIMESTAMP, cJSON_CreateString(timestamp_item->valuestring));
        cJSON_AddItemToObject(object1, STR_TIMESTARTGATHER, cJSON_CreateString(timestartgather_item->valuestring));
        cJSON_AddItemToObject(object1, STR_TIMEENDGATHER, cJSON_CreateString(timeendgather_item->valuestring));
        cJSON_AddItemToObject(object1, STR_ADDITIONALCHECK, cJSON_CreateString(additionalcheck_item->valuestring));
        cJSON *object2 = cJSON_CreateObject();
        cJSON_AddItemToObject(object2, STR_NAME, cJSON_CreateString(name_item->valuestring));
        cJSON_AddItemToObject(object2, STR_VAL, cJSON_CreateString(val_item->valuestring));
        cJSON *object3 = cJSON_CreateArray();
        cJSON_AddItemToArray(object3, object2);
        cJSON_AddItemToObject(object1, STR_BODY, object3);
        cJSON_AddItemToObject(body, STR_BODY, object1);
    }

#if 0   /* 调试 */
    char *payload = cJSON_Print(body);
    printf("func(%s) jn = %s \n\n", __FUNCTION__, payload);
    if (payload) cJSON_free(payload);
#endif

    if (jn) cJSON_Delete(jn);

    return 0;
}


/**
  * @brief  查询历史记录：按第N条记录查询 + 按上N条记录查询
  * @param  null
  * @retval null
  */
static int get_history_orderNo_upperN(cJSON *jn, cJSON *r_body, cJSON *Array, char *frozentype, int start, int end, char **p_err_no)
{
    int i;
    int size;
    cJSON *body_item = NULL;

    if ((NULL == jn) || (NULL == r_body) || (NULL == Array) || (NULL == frozentype)) return -1;

    cJSON *dev_item = cJSON_GetObjectItemCaseSensitive(jn, STR_DEV);
    if ((0 == dev_item) || (0 == dev_item->valuestring) || (0 == strlen(dev_item->valuestring))) {
        *p_err_no = err_table[50];
        return -2;
    }
    //printf("dev = %s \n", dev_item->valuestring);
    char t_name[256];
    memset(t_name, 0x00, sizeof(t_name));
    sprintf(t_name, "%s_history", dev_item->valuestring); /* 历史记录表表名 */

    cJSON_AddItemToObject(r_body, STR_DEV, cJSON_CreateString(dev_item->valuestring));

    if (0 == table_exist_check(t_name)) {
        /* 历史记录表不存在 */
        log_printf(MSG_WARNING, "func(%s) frozen_table_name[%s] dont exist", __FUNCTION__, t_name);
    } else {
        body_item = cJSON_GetObjectItemCaseSensitive(jn, STR_BODY);
        if (0 == body_item) {
            *p_err_no = err_table[2];
            return -2;
        }
        size = cJSON_GetArraySize(body_item);
        //printf("func(%s) cJSON_GetArraySize = %d \n", __FUNCTION__, size);

        for (i = 0; i < size; i++) {
            cJSON * item = cJSON_GetArrayItem(body_item, i);
            if (NULL == item) {
                continue;
            }
            //printf("item = %s \n", item->valuestring);

            get_history_startNo_endNo(t_name, start, end, frozentype, "name", item->valuestring, Array);
        }
    }

    return 0;
}


/* ------------------------------------------------------------------------- */

#define TELEMETRY       "0"     /* 遥测 */
#define TELESCAN        "1"     /* 遥信 */
#define MIN_FROZEN      "2"     /* 分钟冻结数据 */
#define EXTREME_VAL     "3"     /* 极值 */
#define DAY_MONTH_PASS  "4"     /* 日月合格率 */
#define DAY_MONTH_LIMIT "5"     /* 日月越限 */
#define DAY_OVERLOAD    "6"     /* 日重载、日过载 */
#define TROUBLE_REPORT  "7"     /* 故障报告 */

/**
 * @brief  获取子设备状态 处理
 * @param  null
 * @retval null
 */
int topic_get_dev_status(char *app, char *tpc, char *msg, char *response)
{
    ll_t *list = model_list;
    cJSON *r_jn = NULL;
    int rtn = 0;
    char *err_no = NULL;

    log_printf(MSG_DEBUG, "enter %s", __FUNCTION__);

    if ((NULL == app) || (NULL == tpc) || (NULL == msg) || (NULL == response))
    {
        err_no = err_table[2];
        rtn = -1;
        goto CLEAN_UP;
    }

    /* 解析请求的json数据 */
    cJSON *jn = NULL;
    jn = cJSON_Parse(msg);

    cJSON *token_item = NULL;
    token_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TOKEN);
    if (!cJSON_IsString(token_item) || cJSON_IsNull(token_item))
    {
        err_no = err_table[0];
        rtn = -2;
        goto CLEAN_UP;
    }

    cJSON *time_item = NULL;
    time_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TIMESTAMP);
    if (!cJSON_IsString(time_item) || cJSON_IsNull(time_item))
    {
        err_no = err_table[1];
        rtn = -2;
        goto CLEAN_UP;
    }

    cJSON *body_array = NULL;
    body_array = cJSON_GetObjectItemCaseSensitive(jn, STR_BODY);
    if (!cJSON_IsArray(body_array))
    {
        err_no = err_table[2];
        rtn = -2;
        goto CLEAN_UP;
    }

    cJSON *r_body_array = cJSON_CreateArray();           // 准备回复的body
    cJSON *body_array_item = NULL;
    cJSON_ArrayForEach(body_array_item, body_array)
    {
        cJSON *r_inner_body_array = cJSON_CreateArray(); // 保存最内层的body数据

        if(!cJSON_IsObject(body_array_item))
        {
            continue;
        }
        // 获取 dev 字段
        cJSON * dev_name = NULL;
        dev_name = cJSON_GetObjectItemCaseSensitive(body_array_item, STR_DEV);
        if (!cJSON_IsString(dev_name) || cJSON_IsNull(dev_name))
        {
            continue;
        }

        // 从设备名获取模型名
        char model[20] = {'\0'};
        char guid[20] = {'\0'};
        split_dev_string(dev_name->valuestring, model, guid);

        log_printf(MSG_DEBUG, "dev:[%s], model:[%s], guid:[%s]", dev_name->valuestring, model, guid);

        // 获取最里面的 body 字段
        cJSON *inner_body_array = cJSON_GetObjectItemCaseSensitive(body_array_item, STR_BODY);
        if (!cJSON_IsArray(inner_body_array))
        {
            continue;
        }

        cJSON *inner_body_array_item = NULL;
        cJSON_ArrayForEach(inner_body_array_item, inner_body_array)
        {
            if(!cJSON_IsString(inner_body_array_item))
            {
                continue;
            }

            for (int i = 0; i < list->len; i++)
            {
                struct model_list_node_s *p = NULL;
                p = (struct model_list_node_s *)ll_get_n(list, i);
                if ((p) && ((0 == memcmp(p->model_name, model, strlen(p->model_name))) && (strlen(p->model_name) == strlen(model))))
                {
                    // 模型找到
                    if (NULL != p->jn_realtime_data)
                    {
                        cJSON *realtime_data_item = NULL;
                        cJSON_ArrayForEach(realtime_data_item, p->jn_realtime_data)
                        {
                            if (cJSON_IsObject(realtime_data_item))
                            {
                                cJSON *realtime_data_item_dev = cJSON_GetObjectItemCaseSensitive(realtime_data_item, STR_DEV);
                                if(!cJSON_IsString(realtime_data_item_dev) || cJSON_IsNull(realtime_data_item_dev))
                                {
                                    continue;
                                }
                                if ((0 == memcmp(realtime_data_item_dev->valuestring, dev_name->valuestring, strlen(realtime_data_item_dev->valuestring)))
                                    && (strlen(realtime_data_item_dev->valuestring) == strlen(dev_name->valuestring)))
                                {
                                    // 设备找到
                                    // 查找 sub_device_status 数组
                                    cJSON *sub_device_status_array = cJSON_GetObjectItemCaseSensitive(realtime_data_item, STR_SUB_DEVICE_STATUS);

                                    if (cJSON_GetArraySize(sub_device_status_array) == 0)
                                    {
                                        log_printf(MSG_WARNING, "dev:[%s] sub_device_status_array is empty", dev_name->valuestring);
                                        continue;
                                    }

#if 1 /* 调试 */
                                    if (cJSON_IsArray(sub_device_status_array))
                                    {
                                        char *sub_device_status_array_payload = cJSON_Print(sub_device_status_array);
                                        if (sub_device_status_array_payload == NULL)
                                        {
                                            log_printf(MSG_DEBUG, "sub_device_status_array_payload is null");
                                        }
                                        else
                                        {
                                            log_printf(MSG_DEBUG, "sub_device_status_array_payload = %s", sub_device_status_array_payload);
                                        }
                                        if (sub_device_status_array_payload)
                                        {
                                            cJSON_free(sub_device_status_array_payload);
                                        }
                                    }
                                    else
                                    {
                                        log_printf(MSG_DEBUG, "sub_device_status_array_payload is not array");
                                    }
#endif

                                    int flag = 0;
                                    cJSON *sub_device_status_array_item = NULL;
                                    cJSON_ArrayForEach(sub_device_status_array_item, sub_device_status_array)
                                    {
                                        if(!cJSON_IsObject(sub_device_status_array_item) || cJSON_IsNull(sub_device_status_array_item))
                                        {
                                            continue;
                                        }
                                        cJSON *name = cJSON_GetObjectItem(sub_device_status_array_item, "name");
                                        if (cJSON_IsString(name) && !cJSON_IsNull(name))
                                        {
                                            if ((0 == memcmp(name->valuestring, inner_body_array_item->valuestring, strlen(name->valuestring)))
                                                && (strlen(name->valuestring) == strlen(inner_body_array_item->valuestring)))
                                            {
                                                cJSON *copiedObject = NULL;
                                                copiedObject = cJSON_Duplicate(sub_device_status_array_item, 1);
                                                cJSON_AddItemToArray(r_inner_body_array, copiedObject);
                                                flag = 1;
                                            }
                                        }
                                    }
                                    // 这里没有找到，需要回复 null 来表示状态不明确
                                    if(flag == 0)
                                    {
                                        char buff[128] = {'\0'};
                                        get_timestamp(buff);
                                        // 创建一个新的JSON对象
                                        cJSON *r_sub_device_status_array_item = cJSON_CreateObject();
                                        // 向对象中添加字段和值
                                        cJSON_AddItemToObject(r_sub_device_status_array_item, "name", cJSON_CreateString(inner_body_array_item->valuestring));
                                        cJSON_AddItemToObject(r_sub_device_status_array_item, "val", cJSON_CreateString("null"));
                                        cJSON_AddItemToObject(r_sub_device_status_array_item, STR_TIMESTAMP, cJSON_CreateString(buff));
                                        cJSON_AddItemToArray(r_inner_body_array, r_sub_device_status_array_item);
                                    }
                                }
                            }
                        }
                    }
                }
            } // end of for (i = 0; i < list->len; i++)
        }

        cJSON *r_body_object = cJSON_CreateObject();
        cJSON_AddItemToObject(r_body_object, STR_DEV, cJSON_CreateString(dev_name->valuestring));
        cJSON_AddItemToObject(r_body_object, STR_BODY, r_inner_body_array);
        cJSON_AddItemToArray(r_body_array, r_body_object);
    }

    /* 准备回复的数据-------------------------------------------------------- */
    r_jn = cJSON_CreateObject();
    mqtt_ack_msg_prepare(r_jn, token_item->valuestring);
    cJSON_AddItemToObject(r_jn, STR_BODY, r_body_array);

    if (mqtt_ack_msg_send(r_jn, response, app))
    {
        rtn = -3;
        goto CLEAN_UP;
    }

#if 0 /* 调试 */
    char *r_jn_payload = cJSON_Print(r_jn);
    printf("func(%s) r_jn_payload = %s \n", __FUNCTION__, r_jn_payload);
    if (r_jn_payload)
    {
        cJSON_free(r_jn_payload);
    }
#endif

CLEAN_UP:
    mqtt_error_ack(err_no, token_item, response, app);

    if (jn)
    {
        cJSON_Delete(jn);
    }

    if (r_jn)
    {
        cJSON_Delete(r_jn);
    }

    return rtn;
}

/**
 * @brief  监听广播子设备在离线状态
 * @param  null
 * @retval null
 */
int topic_set_dev_status(char *app, char *tpc, char *msg, char *response)
{
    ll_t *list = model_list;
    int rtn = 0;
    char *err_no = NULL;

    log_printf(MSG_DEBUG, "enter %s", __FUNCTION__);

    if ((NULL == app) || (NULL == tpc) || (NULL == msg) || (NULL == response))
    {
        err_no = err_table[2];
        rtn = -1;
        goto CLEAN_UP;
    }

    /* 解析请求的json数据 */
    cJSON *jn = NULL;
    jn = cJSON_Parse(msg);

    cJSON *token_item = NULL;
    token_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TOKEN);
    if (!cJSON_IsString(token_item) || cJSON_IsNull(token_item))
    {
        err_no = err_table[0];
        rtn = -2;
        goto CLEAN_UP;
    }

    cJSON *time_item = NULL;
    time_item = cJSON_GetObjectItemCaseSensitive(jn, STR_TIMESTAMP);
    if (!cJSON_IsString(time_item) || cJSON_IsNull(time_item))
    {
        err_no = err_table[1];
        rtn = -2;
        goto CLEAN_UP;
    }

    cJSON *body_array = NULL;
    body_array = cJSON_GetObjectItemCaseSensitive(jn, STR_BODY);
    if (!cJSON_IsArray(body_array))
    {
        err_no = err_table[2];
        rtn = -2;
        goto CLEAN_UP;
    }

    cJSON *body_array_item = NULL;
    cJSON_ArrayForEach(body_array_item, body_array)
    {
        if (!cJSON_IsObject(body_array_item) || cJSON_IsNull(body_array_item))
        {
            continue;
        }

        // 获取 dev 字段
        cJSON *dev_name = NULL;
        dev_name = cJSON_GetObjectItemCaseSensitive(body_array_item, STR_DEV);
        if (!cJSON_IsString(dev_name) || cJSON_IsNull(dev_name))
        {
            continue;
        }

        // 获取 model 字段
        cJSON *model_name = NULL;
        model_name = cJSON_GetObjectItemCaseSensitive(body_array_item, STR_MODEL);
        if (!cJSON_IsString(model_name) || cJSON_IsNull(model_name))
        {
            continue;
        }

        // 获取 event 字段
        cJSON *event_name = NULL;
        event_name = cJSON_GetObjectItemCaseSensitive(body_array_item, STR_EVENT);
        if (!cJSON_IsString(event_name) || cJSON_IsNull(event_name))
        {
            continue;
        }

        if ((strncmp(event_name->valuestring, "Online", strlen(event_name->valuestring)) != 0) &&
            (strncmp(event_name->valuestring, "Offline", strlen(event_name->valuestring)) != 0))
        {
            continue;
        }

        // 获取 starttime 字段
        cJSON *starttime = NULL;
        starttime = cJSON_GetObjectItemCaseSensitive(body_array_item, "starttime");
        if (!cJSON_IsString(starttime) || cJSON_IsNull(starttime))
        {
            continue;
        }

        for (int i = 0; i < list->len; i++)
        {
            struct model_list_node_s *p = NULL;
            p = (struct model_list_node_s *)ll_get_n(list, i);
            if ((p) && ((0 == memcmp(p->model_name, model_name->valuestring, strlen(p->model_name))) && 
            (strlen(p->model_name) == strlen(model_name->valuestring))))
            {
                // 模型找到
                if (NULL != p->jn_realtime_data)
                {
                    cJSON *realtime_data_item = NULL;
                    cJSON_ArrayForEach(realtime_data_item, p->jn_realtime_data)
                    {
                        if (cJSON_IsObject(realtime_data_item))
                        {
                            cJSON *realtime_data_item_dev = cJSON_GetObjectItemCaseSensitive(realtime_data_item, STR_DEV);
                            if (!cJSON_IsString(realtime_data_item_dev) || cJSON_IsNull(realtime_data_item_dev))
                            {
                                continue;
                            }
                            if ((0 == memcmp(realtime_data_item_dev->valuestring, dev_name->valuestring, strlen(realtime_data_item_dev->valuestring))) && 
                            (strlen(realtime_data_item_dev->valuestring) == strlen(dev_name->valuestring)))
                            {
                                // 设备找到
                                log_printf(MSG_INFO, "func(%s) model[%s] dev[%s] found", __FUNCTION__, model_name->valuestring, dev_name->valuestring);

                                // 如何没有 sub_device_status 数组就创建，有了就不创建
                                // 查找是否有"sub_device_status"的key
                                cJSON *status_array = cJSON_GetObjectItemCaseSensitive(realtime_data_item, STR_SUB_DEVICE_STATUS);
                                if (!cJSON_IsArray(status_array))
                                {
                                    log_printf(MSG_INFO, "sub_device_status array not found, create new sub_device_status array!");
                                    // 如果没有找到，创建一个新的object
                                    status_array = cJSON_CreateArray();
                                }

                                cJSON *status_object = cJSON_CreateObject();
                                if (strncmp(event_name->valuestring, "Online", strlen(event_name->valuestring)) == 0)
                                {
                                    cJSON_AddStringToObject(status_object, STR_NAME, "online");
                                    cJSON_AddStringToObject(status_object, STR_VAL, "1");
                                    cJSON_AddStringToObject(status_object, STR_TIMESTAMP, starttime->valuestring);
                                }
                                else if (strncmp(event_name->valuestring, "Offline", strlen(event_name->valuestring)) == 0)
                                {
                                    cJSON_AddStringToObject(status_object, STR_NAME, "online");
                                    cJSON_AddStringToObject(status_object, STR_VAL, "0");
                                    cJSON_AddStringToObject(status_object, STR_TIMESTAMP, starttime->valuestring);
                                }
                                else
                                {
                                    log_printf(MSG_WARNING, "event_name[%s] is not Online or Offline", event_name->valuestring);
                                    continue;
                                }

                                int flag = 0;
                                cJSON *status_array_item = NULL;
                                cJSON_ArrayForEach(status_array_item, status_array)
                                {
                                    if (cJSON_IsObject(status_array_item))
                                    {
                                        cJSON *status_array_item_name = cJSON_GetObjectItem(status_array_item, "name");
                                        if (cJSON_IsString(status_array_item_name) &&
                                            (0 == memcmp(status_array_item_name->valuestring, "online", strlen("online"))) &&
                                            (strlen(status_array_item_name->valuestring) == strlen("online")))
                                        {
                                            flag = 1; // 找到匹配的键值对，表明已经存在，不需要再添加
                                            // 需要再次更新状态

                                            // 更新 val
                                            cJSON_DeleteItemFromObject(status_array_item, STR_VAL);
                                            cJSON *val = cJSON_GetObjectItem(status_object, STR_VAL);
                                            cJSON_AddStringToObject(status_array_item, STR_VAL, val->valuestring);

                                            // 更新 timestamp
                                            cJSON_DeleteItemFromObject(status_array_item, STR_TIMESTAMP);
                                            cJSON *timestamp = cJSON_GetObjectItem(status_object, STR_TIMESTAMP);
                                            cJSON_AddStringToObject(status_array_item, STR_TIMESTAMP, timestamp->valuestring);
                                        }
                                    }
                                }

                                if (flag == 0)
                                {
                                    // 将status对象添加到status数组中
                                    cJSON_AddItemToArray(status_array, status_object);
                                    // 将status数组添加到dev_item中
                                    cJSON_AddItemToObject(realtime_data_item, STR_SUB_DEVICE_STATUS, status_array);
                                }
#if 1 /* 调试 */
                                char *status_array_payload = cJSON_Print(status_array);
                                log_printf(MSG_DEBUG, "size is (%ld), sub_device_status = %s", strlen(status_array_payload), status_array_payload);
                                if (status_array_payload)
                                {
                                    cJSON_free(status_array_payload);
                                }
#endif
                            }
                        }
                    }
                }
            }
        } // end of for (i = 0; i < list->len; i++)
    }

CLEAN_UP:
    if (jn)
    {
        cJSON_Delete(jn);
    }

    return rtn;
}